SWDEV-432445: ATT continuous mode
Change-Id: I52732fc1dba41df3373ef6a19a428b00d729bf04
[ROCm/rocprofiler commit: aba6a1d986]
This commit is contained in:
@@ -44,13 +44,11 @@
|
||||
#include "rocprofiler.h"
|
||||
#include "rocprofiler_plugin.h"
|
||||
#include "../utils.h"
|
||||
#include "code_printing.hpp"
|
||||
#include "../../src/core/session/att/att.h"
|
||||
#include "../../src/core/session/att/att_header.h"
|
||||
|
||||
#define ATT_FILENAME_MAXBYTES 90
|
||||
#define TEST_INVALID_KERNEL size_t(-1)
|
||||
|
||||
namespace {
|
||||
class att_plugin_t {
|
||||
public:
|
||||
att_plugin_t(void* data) {
|
||||
@@ -65,22 +63,16 @@ class att_plugin_t {
|
||||
|
||||
header.raw = reinterpret_cast<uint64_t>(data);
|
||||
header.reserved = 0x11;
|
||||
|
||||
isa_mode = static_cast<decltype(isa_mode)>(header.isadumpmode);
|
||||
header.isadumpmode = 0;
|
||||
}
|
||||
|
||||
bool MPI_ENABLE = false;
|
||||
int MPI_RANK = 0;
|
||||
std::mutex writing_lock;
|
||||
static std::mutex writing_lock;
|
||||
bool is_valid_{true};
|
||||
rocprofiler::att_header_packet_t header{.raw = 0};
|
||||
rocprofiler::rocprofiler_att_isa_dump_mode isa_mode = rocprofiler::ISA_MODE_DUMP_ALL;
|
||||
|
||||
bool CheckAddrMatches(uint64_t kernel_addr, uint64_t base_address, uint64_t size)
|
||||
{
|
||||
if (isa_mode == rocprofiler::ISA_MODE_DUMP_ALL)
|
||||
return true;
|
||||
return (kernel_addr >= base_address) && (kernel_addr < base_address + size);
|
||||
}
|
||||
|
||||
@@ -93,7 +85,6 @@ class att_plugin_t {
|
||||
|
||||
int FlushATTRecord(const rocprofiler_record_att_tracer_t* att_tracer_record,
|
||||
rocprofiler_session_id_t session_id, rocprofiler_buffer_id_t buffer_id) {
|
||||
std::lock_guard<std::mutex> lock(writing_lock);
|
||||
|
||||
if (!att_tracer_record) return ROCPROFILER_STATUS_ERROR;
|
||||
|
||||
@@ -122,11 +113,11 @@ class att_plugin_t {
|
||||
if (name_demangled.size() > ATT_FILENAME_MAXBYTES) // Limit filename size
|
||||
name_demangled = name_demangled.substr(0, ATT_FILENAME_MAXBYTES);
|
||||
|
||||
std::string outfilepath = ".";
|
||||
if (const char* env = getenv("OUTPUT_PATH")) outfilepath = std::string(env);
|
||||
std::string output_dir = ".";
|
||||
if (const char* env = getenv("OUTPUT_PATH")) output_dir = std::string(env);
|
||||
|
||||
outfilepath.reserve(outfilepath.size() + 128); // Max filename size
|
||||
outfilepath += '/' + name_demangled;
|
||||
std::string outfilepath = output_dir + '/' + name_demangled;
|
||||
outfilepath.reserve(output_dir.size() + 128); // Max filename size
|
||||
if (MPI_ENABLE) outfilepath += "_rank" + std::to_string(MPI_RANK);
|
||||
outfilepath += "_v";
|
||||
|
||||
@@ -139,14 +130,16 @@ class att_plugin_t {
|
||||
auto writer_id = att_tracer_record->writer_id;
|
||||
|
||||
std::string fname = outfilepath + "_kernel.txt";
|
||||
std::ofstream(fname.c_str()) << name_demangled << " dispatch[" << writer_id << "] GPU["
|
||||
<< att_tracer_record->gpu_id.handle << "]: " << kernel_name_mangled
|
||||
<< '\n';
|
||||
std::ofstream kernel_txt_file((outfilepath + "_kernel.txt").c_str());
|
||||
kernel_txt_file << name_demangled << " dispatch[" << writer_id << "] GPU["
|
||||
<< att_tracer_record->gpu_id.handle << "]: " << kernel_name_mangled
|
||||
<< '\n';
|
||||
|
||||
// iterate over each shader engine att trace
|
||||
header.navi = !att_tracer_record->intercept_list.userdata & 0x1;
|
||||
int se_num = att_tracer_record->shader_engine_data_count;
|
||||
for (int i = 0; i < se_num; i++) {
|
||||
for (int i = 0; i < se_num; i++)
|
||||
{
|
||||
if (!att_tracer_record->shader_engine_data ||
|
||||
!att_tracer_record->shader_engine_data[i].buffer_ptr)
|
||||
continue;
|
||||
@@ -166,50 +159,39 @@ class att_plugin_t {
|
||||
out.write(data_buffer_ptr, se_att_trace->buffer_size);
|
||||
}
|
||||
|
||||
if (isa_mode == rocprofiler::ISA_MODE_DUMP_NONE)
|
||||
return 0;
|
||||
for (size_t i = 0; i < att_tracer_record->intercept_list.count; i++)
|
||||
{
|
||||
const auto& symbol = att_tracer_record->intercept_list.symbols[i];
|
||||
if (!symbol.filepath) continue;
|
||||
|
||||
uint64_t kernel_addr = att_tracer_record->intercept_list.userdata >> 1;
|
||||
std::string sfilepath(symbol.filepath);
|
||||
bool bCopiedData = symbol.data && symbol.data_size;
|
||||
|
||||
std::ofstream isafile(outfilepath + "_isa.s");
|
||||
if (!isafile.is_open()) {
|
||||
std::cerr << "Could not open ISA file: " << outfilepath << "_isa.s" << std::endl;
|
||||
return ROCPROFILER_STATUS_ERROR;
|
||||
}
|
||||
isafile << "<Kernel> " << kernel_name_mangled << '\n';
|
||||
if (bCopiedData)
|
||||
{
|
||||
auto pos = sfilepath.find("://");
|
||||
auto rpos = sfilepath.rfind('/');
|
||||
|
||||
for (size_t i = 0; i < att_tracer_record->intercept_list.count; i++) {
|
||||
const rocprofiler_intercepted_codeobj_t& symbol =
|
||||
att_tracer_record->intercept_list.symbols[i];
|
||||
if (pos == std::string::npos || pos+3 >= sfilepath.size()) continue;
|
||||
|
||||
if (!CheckAddrMatches(kernel_addr, symbol.base_address, symbol.mem_size)) continue;
|
||||
|
||||
std::unique_ptr<CodeObjectBinary> binary;
|
||||
std::unique_ptr<code_object_decoder_t> decoder;
|
||||
|
||||
if (symbol.data && symbol.data_size) {
|
||||
decoder = std::make_unique<code_object_decoder_t>(symbol.data, symbol.data_size);
|
||||
} else if (std::string(symbol.filepath).find("file://") != std::string::npos) {
|
||||
binary = std::make_unique<CodeObjectBinary>(symbol.filepath);
|
||||
decoder =
|
||||
std::make_unique<code_object_decoder_t>(binary->buffer.data(), binary->buffer.size());
|
||||
} else {
|
||||
continue;
|
||||
std::string type(sfilepath.begin(), sfilepath.begin()+pos);
|
||||
std::string cut(sfilepath.begin()+rpos+1, sfilepath.end());
|
||||
sfilepath = type + cut + ".out";
|
||||
}
|
||||
|
||||
if (isa_mode == rocprofiler::ISA_MODE_DUMP_KERNEL)
|
||||
decoder->disassemble_single_kernel(kernel_addr-symbol.base_address);
|
||||
else
|
||||
decoder->disassemble_kernels();
|
||||
kernel_txt_file << std::hex << "0x" << symbol.base_address << " 0x" << symbol.mem_size
|
||||
<< ' ' << std::dec << symbol.att_marker_id << ' ' << sfilepath << '\n';
|
||||
|
||||
for (auto& instance : decoder->instructions) {
|
||||
uint64_t addr = instance.address + symbol.base_address;
|
||||
sfilepath = output_dir + '/' + sfilepath;
|
||||
if (!bCopiedData || att_file_exists(sfilepath)) continue;
|
||||
|
||||
if (decoder->m_symbol_map.find(instance.address) != decoder->m_symbol_map.end())
|
||||
isafile << "; Begin " << decoder->m_symbol_map[instance.address].name << '\n';
|
||||
if (instance.cpp_reference) isafile << "; " << instance.cpp_reference << '\n';
|
||||
isafile << instance.instruction << " // " << std::hex << addr << '\n';
|
||||
std::ofstream isafile(sfilepath, std::ios::binary);
|
||||
if (!isafile.is_open()) {
|
||||
std::cerr << "Could not open file: " << sfilepath << std::endl;
|
||||
return ROCPROFILER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
isafile.write(symbol.data, symbol.data_size);
|
||||
}
|
||||
|
||||
return ROCPROFILER_STATUS_SUCCESS;
|
||||
@@ -248,8 +230,7 @@ class att_plugin_t {
|
||||
};
|
||||
|
||||
att_plugin_t* att_plugin = nullptr;
|
||||
|
||||
} // namespace
|
||||
std::mutex att_plugin_t::writing_lock;
|
||||
|
||||
ROCPROFILER_EXPORT int rocprofiler_plugin_initialize(uint32_t rocprofiler_major_version,
|
||||
uint32_t rocprofiler_minor_version,
|
||||
@@ -258,6 +239,7 @@ ROCPROFILER_EXPORT int rocprofiler_plugin_initialize(uint32_t rocprofiler_major_
|
||||
rocprofiler_minor_version < ROCPROFILER_VERSION_MINOR)
|
||||
return ROCPROFILER_STATUS_ERROR;
|
||||
|
||||
std::lock_guard<std::mutex> lock(att_plugin_t::writing_lock);
|
||||
if (att_plugin != nullptr) return ROCPROFILER_STATUS_ERROR;
|
||||
|
||||
att_plugin = new att_plugin_t(data);
|
||||
@@ -270,6 +252,7 @@ ROCPROFILER_EXPORT int rocprofiler_plugin_initialize(uint32_t rocprofiler_major_
|
||||
}
|
||||
|
||||
ROCPROFILER_EXPORT void rocprofiler_plugin_finalize() {
|
||||
std::lock_guard<std::mutex> lock(att_plugin_t::writing_lock);
|
||||
if (!att_plugin) return;
|
||||
delete att_plugin;
|
||||
att_plugin = nullptr;
|
||||
@@ -278,11 +261,13 @@ ROCPROFILER_EXPORT void rocprofiler_plugin_finalize() {
|
||||
ROCPROFILER_EXPORT int rocprofiler_plugin_write_buffer_records(
|
||||
const rocprofiler_record_header_t* begin, const rocprofiler_record_header_t* end,
|
||||
rocprofiler_session_id_t session_id, rocprofiler_buffer_id_t buffer_id) {
|
||||
std::lock_guard<std::mutex> lock(att_plugin_t::writing_lock);
|
||||
if (!att_plugin || !att_plugin->IsValid()) return ROCPROFILER_STATUS_ERROR;
|
||||
return att_plugin->WriteBufferRecords(begin, end, session_id, buffer_id);
|
||||
}
|
||||
|
||||
ROCPROFILER_EXPORT int rocprofiler_plugin_write_record(rocprofiler_record_tracer_t record) {
|
||||
std::lock_guard<std::mutex> lock(att_plugin_t::writing_lock);
|
||||
if (!att_plugin || !att_plugin->IsValid()) return ROCPROFILER_STATUS_ERROR;
|
||||
if (record.header.id.handle == 0) return ROCPROFILER_STATUS_SUCCESS;
|
||||
return ROCPROFILER_STATUS_SUCCESS;
|
||||
|
||||
@@ -17,6 +17,7 @@ import numpy as np
|
||||
from stitch import stitch
|
||||
import gc
|
||||
from collections import defaultdict
|
||||
from service import CodeobjService
|
||||
|
||||
ATT_VERSION = 2
|
||||
|
||||
@@ -189,15 +190,10 @@ def parse_binary(filename, kernel=None):
|
||||
info = SO.wrapped_parse_binary(str(filename).encode("utf-8"), kernel)
|
||||
|
||||
code = []
|
||||
kernel_addr = defaultdict(lambda : "Unknown")
|
||||
last_known_function = "Unknown"
|
||||
for k in range(info.code_len):
|
||||
code_entry = info.code[k]
|
||||
|
||||
line = deepcopy(code_entry.line.decode("utf-8"))
|
||||
if "; Begin " in line:
|
||||
last_known_function = line.split("; Begin ")[1]
|
||||
|
||||
loc = deepcopy(code_entry.loc.decode("utf-8"))
|
||||
|
||||
to_line = int(code_entry.to_line) if (code_entry.to_line >= 0) else None
|
||||
@@ -207,14 +203,11 @@ def parse_binary(filename, kernel=None):
|
||||
code.append([line, int(code_entry.value), to_line, loc, int(code_entry.index),
|
||||
int(code_entry.line_num), int(code_entry.addr), 0, 0])
|
||||
|
||||
if code[-1][-3] != 0 and len(code) > 1:
|
||||
kernel_addr[code[-1][-3]] = last_known_function
|
||||
|
||||
jumps = {}
|
||||
for k in range(info.jumps_len):
|
||||
jumps[info.jumps[k].key] = info.jumps[k].value
|
||||
|
||||
return code, jumps, kernel_addr
|
||||
return code, jumps
|
||||
|
||||
|
||||
def getWaves_binary(name):
|
||||
@@ -248,13 +241,17 @@ def getWaves_binary(name):
|
||||
return (traces_python, waves_python, events, occupancy, flags, kernel_addr, info.flags & 0x4)
|
||||
|
||||
|
||||
def getWaves_stitch(traces, code, jumps, flags, latency_map, hitcount_map, bIsAuto):
|
||||
def getWaves_stitch(traces, code, jumps, flags, latency_map, hitcount_map, bIsAuto, codeobjservice):
|
||||
for id in traces.keys():
|
||||
traces[id].instructions = stitch(traces[id].instructions, code, jumps, flags, bIsAuto)
|
||||
traces[id].instructions = stitch(traces[id].instructions, code, jumps, flags, bIsAuto, codeobjservice)
|
||||
if len(code) > hitcount_map.size:
|
||||
hitcount_map = np.pad(hitcount_map, [0,len(code)-hitcount_map.size])
|
||||
latency_map = np.pad(latency_map, [0,len(code)-latency_map.size])
|
||||
if traces[id].instructions is not None:
|
||||
for inst in traces[id].instructions[0]:
|
||||
hitcount_map[inst.asmline] += inst.num_waves
|
||||
latency_map[inst.asmline] += inst.cycles
|
||||
return hitcount_map, latency_map
|
||||
|
||||
|
||||
def persist(trace_file, SIMD, traces):
|
||||
@@ -301,7 +298,7 @@ def persist(trace_file, SIMD, traces):
|
||||
skips = traces[wave.traceid].instructions[-1]
|
||||
try:
|
||||
for v in traces[wave.traceid].instructions[0]:
|
||||
if cc in skips:
|
||||
while cc in skips:
|
||||
cc += 1
|
||||
t = wave.instructions[cc]
|
||||
insts.append((t[0], v.type, 0, t[1], v.asmline))
|
||||
@@ -434,7 +431,7 @@ if __name__ == "__main__":
|
||||
"--trace_file", help="Filter for trace files", default=None, type=str
|
||||
)
|
||||
parser.add_argument(
|
||||
"--att_kernel", help="Kernel file", type=str, default=pathenv + "/*_kernel.txt"
|
||||
"--att_kernel", help="Kernel file", type=str, default=os.path.join(pathenv, "*_kernel.txt")
|
||||
)
|
||||
parser.add_argument("--ports", help="Server and websocket ports, default: 8000,18000")
|
||||
parser.add_argument(
|
||||
@@ -480,17 +477,28 @@ if __name__ == "__main__":
|
||||
|
||||
for att_kernel in att_kernel_list:
|
||||
print('Parsing:', att_kernel)
|
||||
att_kernel_f = []
|
||||
with open(att_kernel, 'r') as f:
|
||||
for line in f:
|
||||
att_kernel_f.append(line.split('\n')[0])
|
||||
|
||||
get_path_loc = lambda x: np.sum([len(m) for m in x.split(' ')[:3]])+3
|
||||
add_pathnv = lambda x: x[:get_path_loc(x)] + os.path.join(pathenv, x[get_path_loc(x):])
|
||||
|
||||
att_kernel_f = [add_pathnv(p) if '.out' == p[-4:] else p for p in att_kernel_f[1:]]
|
||||
assembly_code = deepcopy(args.assembly_code)
|
||||
|
||||
# Assembly parsing
|
||||
bIsAuto = False
|
||||
if assembly_code.lower().strip() == 'auto':
|
||||
assembly_code = att_kernel.split('_kernel.txt')[0]+'_isa.s'
|
||||
bIsAuto = True
|
||||
path = Path(assembly_code)
|
||||
if not path.is_file():
|
||||
code = [['; Begin ATT ASM', 100, 0, '', 0, 0, 0, 0, 0]]
|
||||
jumps = []
|
||||
elif not Path(assembly_code).is_file():
|
||||
print("Invalid assembly_code('{0}')!".format(assembly_code))
|
||||
sys.exit(1)
|
||||
else:
|
||||
code, jumps = parse_binary(assembly_code, att_kernel)
|
||||
|
||||
# Trace Parsing
|
||||
trace_instance_name = att_kernel.split("_kernel.txt")[0]
|
||||
@@ -504,7 +512,6 @@ if __name__ == "__main__":
|
||||
continue
|
||||
|
||||
print('Att kernel:', att_kernel)
|
||||
code, jumps, kern_addr = parse_binary(assembly_code, None if bIsAuto else att_kernel)
|
||||
|
||||
DBFILES = []
|
||||
EVENTS = []
|
||||
@@ -512,12 +519,16 @@ if __name__ == "__main__":
|
||||
GFXV = []
|
||||
analysed_filenames = []
|
||||
occupancy_filenames = []
|
||||
dispatch_kernel_names = {}
|
||||
kernel_addr = {}
|
||||
|
||||
latency_map = np.zeros((len(code)), dtype=np.int64)
|
||||
hitcount_map = np.zeros((len(code)), dtype=np.int32)
|
||||
|
||||
gc.collect()
|
||||
if bIsAuto:
|
||||
codeservice = CodeobjService(att_kernel_f, SO.classify_asm_line)
|
||||
else:
|
||||
codeservice = None
|
||||
|
||||
for name in filenames:
|
||||
traces, waves, perfevents, occupancy, gfxv, addrs, ftrace = getWaves_binary(name)
|
||||
@@ -528,7 +539,7 @@ if __name__ == "__main__":
|
||||
assert False
|
||||
|
||||
for id, addr in enumerate(addrs):
|
||||
dispatch_kernel_names[id] = kern_addr[addr]
|
||||
kernel_addr[id] = addr
|
||||
if len(occupancy) > 16:
|
||||
OCCUPANCY.append( occupancy )
|
||||
occupancy_filenames.append(name)
|
||||
@@ -537,7 +548,7 @@ if __name__ == "__main__":
|
||||
print("No traces from", name)
|
||||
continue
|
||||
|
||||
getWaves_stitch(traces, code, jumps, gfxv, latency_map, hitcount_map, bIsAuto)
|
||||
hitcount_map, latency_map = getWaves_stitch(traces, code, jumps, gfxv, latency_map, hitcount_map, bIsAuto, codeservice)
|
||||
|
||||
analysed_filenames.append(name)
|
||||
EVENTS.append(perfevents)
|
||||
@@ -559,7 +570,7 @@ if __name__ == "__main__":
|
||||
"EVENT_NAMES": EVENT_NAMES,
|
||||
"OCCUPANCY": OCCUPANCY,
|
||||
"ShaderNames": occupancy_filenames,
|
||||
"DispatchNames": dispatch_kernel_names,
|
||||
"DispatchNames": {id: codeservice.getSymbolName(addr) for id, addr in kernel_addr.items()}
|
||||
}
|
||||
view_trace(
|
||||
args,
|
||||
|
||||
@@ -49,6 +49,26 @@
|
||||
#include <elfutils/libdw.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#define C_API_BEGIN try {
|
||||
|
||||
#define C_API_END(returndata) \
|
||||
} catch (std::exception& e) \
|
||||
{ \
|
||||
std::cerr << "Error: " << e.what() << std::endl; \
|
||||
return returndata; \
|
||||
} \
|
||||
catch (std::string& s) \
|
||||
{ \
|
||||
std::cerr << "Error: " << s << std::endl; \
|
||||
return returndata; \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
return returndata; \
|
||||
}
|
||||
|
||||
code_object_decoder_t::code_object_decoder_t(const char* codeobj_data, uint64_t codeobj_size) {
|
||||
buffer = std::vector<char>{};
|
||||
buffer.resize(codeobj_size);
|
||||
@@ -110,8 +130,16 @@ code_object_decoder_t::code_object_decoder_t(const char* codeobj_data, uint64_t
|
||||
// load_symbol_map();
|
||||
}
|
||||
|
||||
disassembly = std::make_unique<DisassemblyInstance>(*this);
|
||||
m_symbol_map = disassembly->GetKernelMap();
|
||||
try {
|
||||
disassembly = std::make_unique<DisassemblyInstance>(*this); // Can throw
|
||||
} catch(std::exception& e) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m_symbol_map = disassembly->GetKernelMap(); // Can throw
|
||||
} catch(std::exception& e) {
|
||||
return;
|
||||
}
|
||||
|
||||
//disassemble_kernels();
|
||||
}
|
||||
@@ -143,7 +171,29 @@ std::optional<SymbolInfo> code_object_decoder_t::find_symbol(uint64_t vaddr) {
|
||||
return SymbolInfo{symbol_name, symbol.faddr, symbol.mem_size};
|
||||
}
|
||||
|
||||
void code_object_decoder_t::disassemble_kernel(uint64_t faddr, uint64_t vaddr) {
|
||||
std::pair<instruction_instance_t, size_t>
|
||||
code_object_decoder_t::disassemble_instruction(uint64_t faddr, uint64_t vaddr)
|
||||
{
|
||||
if (!disassembly)
|
||||
throw std::exception();
|
||||
|
||||
char* cpp_line = nullptr;
|
||||
auto it = m_line_number_map.find(vaddr);
|
||||
if (it != m_line_number_map.end()) {
|
||||
const std::string& file_name = it->second.first;
|
||||
size_t line_number = it->second.second;
|
||||
|
||||
std::string cpp = file_name + ':' + std::to_string(line_number);
|
||||
cpp_line = (char*)calloc(cpp.size() + 4, sizeof(char));
|
||||
std::memcpy(cpp_line, cpp.data(), cpp.size() * sizeof(char));
|
||||
}
|
||||
size_t size = disassembly->ReadInstruction(faddr, vaddr, cpp_line);
|
||||
return {disassembly->last_instruction, size};
|
||||
}
|
||||
|
||||
void code_object_decoder_t::disassemble_kernel(uint64_t faddr, uint64_t vaddr)
|
||||
{
|
||||
if (!disassembly) return;
|
||||
auto symbol = find_symbol(vaddr);
|
||||
|
||||
if (!symbol)
|
||||
@@ -155,19 +205,12 @@ void code_object_decoder_t::disassemble_kernel(uint64_t faddr, uint64_t vaddr) {
|
||||
std::cout << "Dumping ISA for " << symbol->name << std::endl;
|
||||
|
||||
uint64_t end_addr = faddr + symbol->mem_size;
|
||||
while (faddr < end_addr) {
|
||||
char* cpp_line = nullptr;
|
||||
auto it = m_line_number_map.find(vaddr);
|
||||
if (it != m_line_number_map.end()) {
|
||||
const std::string& file_name = it->second.first;
|
||||
size_t line_number = it->second.second;
|
||||
|
||||
std::string cpp = file_name + ':' + std::to_string(line_number);
|
||||
cpp_line = (char*)calloc(cpp.size() + 4, sizeof(char));
|
||||
std::memcpy(cpp_line, cpp.data(), cpp.size() * sizeof(char));
|
||||
}
|
||||
|
||||
size_t size = disassembly->ReadInstruction(faddr, vaddr, cpp_line);
|
||||
while (faddr < end_addr)
|
||||
{
|
||||
size_t size;
|
||||
instruction_instance_t inst;
|
||||
std::tie(inst, size) = this->disassemble_instruction(faddr, vaddr);
|
||||
instructions.push_back(inst);
|
||||
faddr += size;
|
||||
vaddr += size;
|
||||
}
|
||||
@@ -182,3 +225,136 @@ void code_object_decoder_t::disassemble_single_kernel(uint64_t kaddr) {
|
||||
if (kaddr >= vaddr && kaddr < vaddr + v.mem_size)
|
||||
disassemble_kernel(v.faddr, vaddr);
|
||||
}
|
||||
|
||||
CodeobjService::CodeobjService(const char* filepath, uint64_t load_base): load_base(load_base)
|
||||
{
|
||||
if (!filepath) throw "Empty filepath.";
|
||||
|
||||
std::string_view fpath(filepath);
|
||||
|
||||
if (fpath.rfind(".out") + 4 == fpath.size())
|
||||
{
|
||||
std::ifstream file(filepath, std::ios::in | std::ios::binary);
|
||||
|
||||
if (!file.is_open())
|
||||
throw "Invalid filename " + std::string(filepath);
|
||||
|
||||
std::vector<char> buffer;
|
||||
file.seekg(0, file.end);
|
||||
buffer.resize(file.tellg());
|
||||
file.seekg(0, file.beg);
|
||||
file.read(buffer.data(), buffer.size());
|
||||
|
||||
decoder = std::make_unique<code_object_decoder_t>(buffer.data(), buffer.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_ptr<CodeObjectBinary> binary = std::make_unique<CodeObjectBinary>(filepath);
|
||||
decoder = std::make_unique<code_object_decoder_t>(binary->buffer.data(), binary->buffer.size());
|
||||
}
|
||||
}
|
||||
|
||||
bool CodeobjService::decode_single(uint64_t vaddr, uint64_t faddr)
|
||||
{
|
||||
if (!decoder->disassembly) return false;
|
||||
|
||||
try
|
||||
{
|
||||
decoded_map[vaddr] = decoder->disassemble_instruction(faddr, vaddr-load_base);
|
||||
}
|
||||
catch(std::exception& e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<instruction_instance_t, size_t>& CodeobjService::getDecoded(uint64_t addr)
|
||||
{
|
||||
if (decoded_map.find(addr) != decoded_map.end())
|
||||
return decoded_map[addr];
|
||||
|
||||
std::optional<uint64_t> faddr{};
|
||||
|
||||
if (!bNotElfFILE)
|
||||
{
|
||||
faddr = DisassemblyInstance::va2fo(decoder->buffer.data(), addr-load_base);
|
||||
if (!faddr)
|
||||
bNotElfFILE = true;
|
||||
}
|
||||
|
||||
if (bNotElfFILE && decoder->buffer.size() > 0x100) {
|
||||
uint64_t f_offset = *reinterpret_cast<uint32_t*>(decoder->buffer.data()+0xb8);
|
||||
uint64_t v_offset = *reinterpret_cast<uint32_t*>(decoder->buffer.data()+0xc8);
|
||||
|
||||
faddr = addr+f_offset-load_base-v_offset;
|
||||
}
|
||||
|
||||
if (!faddr || !decode_single(addr, *faddr))
|
||||
{
|
||||
std::cerr << "Invalid addr: " << std::hex << addr << std::dec << std::endl;
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
return decoded_map[addr];
|
||||
}
|
||||
|
||||
std::unordered_map<uint64_t, std::unique_ptr<CodeobjService>> services{};
|
||||
std::atomic<uint64_t> shandles{1};
|
||||
|
||||
#define PUBLIC_API __attribute__((visibility("default")))
|
||||
|
||||
extern "C"
|
||||
{
|
||||
PUBLIC_API uint64_t createService(const char* filename, uint64_t load_base)
|
||||
{
|
||||
C_API_BEGIN
|
||||
|
||||
uint64_t handle = shandles.fetch_add(1);
|
||||
services[handle] = std::make_unique<CodeobjService>(filename, load_base);
|
||||
return handle;
|
||||
|
||||
C_API_END(0)
|
||||
}
|
||||
PUBLIC_API int deleteService(uint64_t handle)
|
||||
{
|
||||
return services.erase(handle);
|
||||
}
|
||||
PUBLIC_API const char* getInstruction(uint64_t handle, uint64_t addr)
|
||||
{
|
||||
C_API_BEGIN
|
||||
|
||||
return services.at(handle)->getInstruction(addr);
|
||||
|
||||
C_API_END(nullptr)
|
||||
}
|
||||
PUBLIC_API const char* getCppref(uint64_t handle, uint64_t addr)
|
||||
{
|
||||
C_API_BEGIN
|
||||
|
||||
return services.at(handle)->getCppref(addr);
|
||||
|
||||
C_API_END(nullptr)
|
||||
}
|
||||
PUBLIC_API size_t getInstSize(uint64_t handle, uint64_t addr)
|
||||
{
|
||||
C_API_BEGIN
|
||||
|
||||
return services.at(handle)->getSize(addr);
|
||||
|
||||
C_API_END(0)
|
||||
}
|
||||
PUBLIC_API const char* getSymbolName(uint64_t addr)
|
||||
{
|
||||
C_API_BEGIN
|
||||
|
||||
for (auto& [handle, service] : services)
|
||||
{
|
||||
if (!service->inrange(addr)) continue;
|
||||
return service->getSymbolName(addr);
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
C_API_END(nullptr)
|
||||
}
|
||||
}
|
||||
@@ -27,28 +27,67 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "disassembly.hpp"
|
||||
|
||||
class code_object_decoder_t {
|
||||
public:
|
||||
// void load_symbol_map();
|
||||
public:
|
||||
std::optional<SymbolInfo> find_symbol(uint64_t address);
|
||||
|
||||
code_object_decoder_t(const char* codeobj_data, uint64_t codeobj_size);
|
||||
~code_object_decoder_t();
|
||||
|
||||
std::pair<instruction_instance_t, size_t>
|
||||
disassemble_instruction(uint64_t faddr, uint64_t vaddr);
|
||||
void disassemble_kernel(uint64_t faddr, uint64_t vaddr);
|
||||
void disassemble_single_kernel(uint64_t kaddr);
|
||||
void disassemble_kernels();
|
||||
|
||||
int m_fd;
|
||||
|
||||
std::map<uint64_t, std::pair<std::string, size_t>> m_line_number_map;
|
||||
std::map<uint64_t, SymbolInfo> m_symbol_map;
|
||||
std::map<uint64_t, std::pair<std::string, size_t>> m_line_number_map{};
|
||||
std::map<uint64_t, SymbolInfo> m_symbol_map{};
|
||||
|
||||
std::string m_uri;
|
||||
std::vector<char> buffer;
|
||||
std::vector<instruction_instance_t> instructions;
|
||||
std::unique_ptr<DisassemblyInstance> disassembly;
|
||||
std::vector<instruction_instance_t> instructions{};
|
||||
std::unique_ptr<DisassemblyInstance> disassembly{};
|
||||
};
|
||||
|
||||
class CodeobjService
|
||||
{
|
||||
public:
|
||||
CodeobjService(const char* filepath, uint64_t load_base);
|
||||
bool decode_single(uint64_t vaddr, uint64_t faddr);
|
||||
|
||||
std::pair<instruction_instance_t, size_t>& getDecoded(uint64_t addr);
|
||||
const char* getInstruction(uint64_t addr) { return getDecoded(addr).first.instruction; }
|
||||
const char* getCppref(uint64_t addr) { return getDecoded(addr).first.cpp_reference; }
|
||||
size_t getSize(uint64_t addr) { return getDecoded(addr).second; }
|
||||
|
||||
uint64_t size() const {
|
||||
if (!decoder) return 0;
|
||||
return decoder->buffer.size();
|
||||
}
|
||||
uint64_t begin() const { return load_base; };
|
||||
uint64_t end() const { return begin() + size(); }
|
||||
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_base);
|
||||
if (it != decoder->m_symbol_map.end())
|
||||
return it->second.name.data();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
private:
|
||||
const uint64_t load_base;
|
||||
|
||||
std::unordered_map<uint64_t, std::pair<instruction_instance_t, size_t>> decoded_map;
|
||||
std::unique_ptr<code_object_decoder_t> decoder{nullptr};
|
||||
|
||||
bool bNotElfFILE = false;
|
||||
};
|
||||
|
||||
@@ -52,14 +52,23 @@
|
||||
#include "code_printing.hpp"
|
||||
#include <hsa/amd_hsa_elf.h>
|
||||
|
||||
#define CHECK_COMGR(call) \
|
||||
if (amd_comgr_status_s status = call) { \
|
||||
const char* reason = ""; \
|
||||
amd_comgr_status_string(status, &reason); \
|
||||
std::cerr << __LINE__ << " code: " << status << std::endl; \
|
||||
std::cerr << __LINE__ << " failed: " << reason << std::endl; \
|
||||
exit(1); \
|
||||
}
|
||||
#define THROW_COMGR(call) \
|
||||
if (amd_comgr_status_s status = call) { \
|
||||
const char* reason = ""; \
|
||||
amd_comgr_status_string(status, &reason); \
|
||||
std::cerr << __FILE__ << ':' << __LINE__ << " code: " \
|
||||
<< status << " failed: " << reason << std::endl; \
|
||||
throw std::exception(); \
|
||||
}
|
||||
|
||||
#define RETURN_COMGR(call) \
|
||||
if (amd_comgr_status_s status = call) { \
|
||||
const char* reason = ""; \
|
||||
amd_comgr_status_string(status, &reason); \
|
||||
std::cerr << __FILE__ << ':' << __LINE__ << " code: " \
|
||||
<< status << " failed: " << reason << std::endl; \
|
||||
return AMD_COMGR_STATUS_ERROR; \
|
||||
}
|
||||
|
||||
CodeObjectBinary::CodeObjectBinary(const std::string& uri) : m_uri(uri) {
|
||||
const std::string protocol_delim{"://"};
|
||||
@@ -83,12 +92,14 @@ CodeObjectBinary::CodeObjectBinary(const std::string& uri) : m_uri(uri) {
|
||||
std::string decoded_path;
|
||||
decoded_path.reserve(path.length());
|
||||
for (size_t i = 0; i < path.length(); ++i)
|
||||
{
|
||||
if (path[i] == '%' && std::isxdigit(path[i + 1]) && std::isxdigit(path[i + 2])) {
|
||||
decoded_path += std::stoi(path.substr(i + 1, 2), 0, 16);
|
||||
i += 2;
|
||||
} else {
|
||||
decoded_path += path[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Tokenize the query/fragment. */
|
||||
std::vector<std::string> tokens;
|
||||
@@ -122,26 +133,19 @@ CodeObjectBinary::CodeObjectBinary(const std::string& uri) : m_uri(uri) {
|
||||
if (!(size = std::stoul(size_it->second, nullptr, 0))) return;
|
||||
}
|
||||
|
||||
if (protocol != "file") {
|
||||
printf("\"%s\" protocol not supported\n", protocol.c_str());
|
||||
return;
|
||||
}
|
||||
if (protocol != "file") throw protocol + " protocol not supported!";
|
||||
|
||||
std::ifstream file(decoded_path, std::ios::in | std::ios::binary);
|
||||
if (!file) {
|
||||
printf("could not open `%s'\n", decoded_path.c_str());
|
||||
return;
|
||||
}
|
||||
if (!file || !file.is_open()) throw "could not open " + decoded_path;
|
||||
|
||||
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", decoded_path.c_str());
|
||||
return;
|
||||
}
|
||||
if (bytes < offset)
|
||||
throw "invalid uri " + decoded_path + " (file size < offset)";
|
||||
|
||||
size = bytes - offset;
|
||||
}
|
||||
|
||||
@@ -154,17 +158,20 @@ CodeObjectBinary::CodeObjectBinary(const std::string& uri) : m_uri(uri) {
|
||||
|
||||
DisassemblyInstance::DisassemblyInstance(code_object_decoder_t& decoder)
|
||||
: buffer(reinterpret_cast<void*>(decoder.buffer.data())),
|
||||
size(decoder.buffer.size()),
|
||||
instructions(decoder.instructions) {
|
||||
CHECK_COMGR(amd_comgr_create_data(AMD_COMGR_DATA_KIND_EXECUTABLE, &data));
|
||||
CHECK_COMGR(amd_comgr_set_data(data, size, decoder.buffer.data()));
|
||||
size(decoder.buffer.size())
|
||||
{
|
||||
THROW_COMGR(amd_comgr_create_data(AMD_COMGR_DATA_KIND_EXECUTABLE, &data));
|
||||
THROW_COMGR(amd_comgr_set_data(data, size, decoder.buffer.data()));
|
||||
|
||||
/*std::cout << "checking isa" << std::endl;
|
||||
char isa_name[128];
|
||||
size_t isa_size = sizeof(isa_name);
|
||||
CHECK_COMGR(amd_comgr_get_data_isa_name(data, &isa_size, isa_name));
|
||||
std::cout << isa_name << std::endl; */
|
||||
const char* isa_name = "amdgcn-amd-amdhsa--gfx1100";
|
||||
|
||||
CHECK_COMGR(amd_comgr_create_disassembly_info(
|
||||
isa_name, //"amdgcn-amd-amdhsa--gfx1100",
|
||||
THROW_COMGR(amd_comgr_create_disassembly_info(
|
||||
isa_name,
|
||||
&DisassemblyInstance::memory_callback, &DisassemblyInstance::inst_callback,
|
||||
[](uint64_t address, void* user_data) {}, &info));
|
||||
}
|
||||
@@ -183,7 +190,7 @@ static bool IsKernelType(amd_comgr_symbol_type_t type)
|
||||
amd_comgr_status_t DisassemblyInstance::symbol_callback(amd_comgr_symbol_t symbol,
|
||||
void* user_data) {
|
||||
amd_comgr_symbol_type_t type;
|
||||
CHECK_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_TYPE, &type));
|
||||
RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_TYPE, &type));
|
||||
|
||||
if (!IsKernelType(type))
|
||||
return AMD_COMGR_STATUS_SUCCESS;
|
||||
@@ -191,14 +198,14 @@ amd_comgr_status_t DisassemblyInstance::symbol_callback(amd_comgr_symbol_t symbo
|
||||
uint64_t vaddr;
|
||||
uint64_t mem_size;
|
||||
uint64_t name_size;
|
||||
CHECK_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_VALUE, &vaddr));
|
||||
CHECK_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_SIZE, &mem_size));
|
||||
CHECK_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME_LENGTH, &name_size));
|
||||
RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_VALUE, &vaddr));
|
||||
RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_SIZE, &mem_size));
|
||||
RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME_LENGTH, &name_size));
|
||||
|
||||
std::string name;
|
||||
name.resize(name_size);
|
||||
|
||||
CHECK_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME, name.data()));
|
||||
RETURN_COMGR(amd_comgr_symbol_get_info(symbol, AMD_COMGR_SYMBOL_INFO_NAME, name.data()));
|
||||
|
||||
DisassemblyInstance& instance = *static_cast<DisassemblyInstance*>(user_data);
|
||||
std::optional<uint64_t> faddr = va2fo(instance.buffer, vaddr);
|
||||
@@ -210,23 +217,25 @@ amd_comgr_status_t DisassemblyInstance::symbol_callback(amd_comgr_symbol_t symbo
|
||||
|
||||
std::map<uint64_t, SymbolInfo>& DisassemblyInstance::GetKernelMap() {
|
||||
symbol_map = {};
|
||||
CHECK_COMGR(amd_comgr_iterate_symbols(data, &DisassemblyInstance::symbol_callback, this));
|
||||
THROW_COMGR(amd_comgr_iterate_symbols(data, &DisassemblyInstance::symbol_callback, this));
|
||||
|
||||
return symbol_map;
|
||||
}
|
||||
|
||||
DisassemblyInstance::~DisassemblyInstance() {
|
||||
CHECK_COMGR(amd_comgr_release_data(data));
|
||||
CHECK_COMGR(amd_comgr_destroy_disassembly_info(info));
|
||||
amd_comgr_release_data(data);
|
||||
amd_comgr_destroy_disassembly_info(info);
|
||||
}
|
||||
|
||||
uint64_t DisassemblyInstance::ReadInstruction(uint64_t faddr, uint64_t vaddr, const char* cpp_line)
|
||||
{
|
||||
uint64_t size_read;
|
||||
uint64_t addr_in_buffer = reinterpret_cast<uint64_t>(buffer) + faddr;
|
||||
CHECK_COMGR(amd_comgr_disassemble_instruction(info, addr_in_buffer, (void*)this, &size_read));
|
||||
assert(instructions.size() != 0);
|
||||
instructions.back().address = vaddr;
|
||||
instructions.back().cpp_reference = cpp_line;
|
||||
|
||||
THROW_COMGR(amd_comgr_disassemble_instruction(info, addr_in_buffer, (void*)this, &size_read));
|
||||
|
||||
last_instruction.address = vaddr;
|
||||
last_instruction.cpp_reference = cpp_line;
|
||||
return size_read;
|
||||
}
|
||||
|
||||
@@ -241,7 +250,7 @@ uint64_t DisassemblyInstance::memory_callback(uint64_t from, char* to, uint64_t
|
||||
|
||||
void DisassemblyInstance::inst_callback(const char* instruction, void* user_data) {
|
||||
DisassemblyInstance& instance = *static_cast<DisassemblyInstance*>(user_data);
|
||||
instance.instructions.push_back({strdup(instruction), nullptr, 0});
|
||||
instance.last_instruction.instruction = strdup(instruction);
|
||||
}
|
||||
|
||||
#define CHECK_VA2FO(x, msg) if (!(x)) { \
|
||||
|
||||
@@ -61,7 +61,7 @@ class DisassemblyInstance {
|
||||
|
||||
void* buffer;
|
||||
int64_t size;
|
||||
std::vector<instruction_instance_t>& instructions;
|
||||
instruction_instance_t last_instruction;
|
||||
amd_comgr_disassembly_info_t info;
|
||||
amd_comgr_data_t data;
|
||||
std::map<uint64_t, SymbolInfo> symbol_map;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import ctypes
|
||||
from ctypes import *
|
||||
|
||||
pluginpath = '/home/giovanni/Desktop/rocprofiler/build/lib/rocprofiler/libatt_plugin.so'
|
||||
|
||||
attplugin = ctypes.CDLL(pluginpath)
|
||||
|
||||
attplugin.createService.restype = ctypes.c_uint64
|
||||
attplugin.createService.argtypes = [ctypes.c_char_p, ctypes.c_uint64]
|
||||
attplugin.deleteService.restype = ctypes.c_int
|
||||
attplugin.deleteService.argtypes = [ctypes.c_uint64]
|
||||
attplugin.getInstruction.restype = ctypes.c_char_p
|
||||
attplugin.getInstruction.argtypes = [ctypes.c_uint64, ctypes.c_uint64]
|
||||
attplugin.getCppref.restype = ctypes.c_char_p
|
||||
attplugin.getCppref.argtypes = [ctypes.c_uint64, ctypes.c_uint64]
|
||||
attplugin.getInstSize.restype = ctypes.c_size_t
|
||||
attplugin.getInstSize.argtypes = [ctypes.c_uint64, ctypes.c_uint64]
|
||||
attplugin.getSymbolName.restype = ctypes.c_char_p
|
||||
attplugin.getSymbolName.argtypes = [ctypes.c_uint64]
|
||||
|
||||
|
||||
class CodeobjInstance:
|
||||
def __init__(self, line, classification_func):
|
||||
tokens = line.split(' ')
|
||||
self.load_base = int(tokens[0], 16)
|
||||
self.load_end = self.load_base + int(tokens[1], 16)
|
||||
self.att_marker = int(tokens[2])
|
||||
self.fpath = tokens[3]
|
||||
|
||||
self.handle = attplugin.createService(self.fpath.encode('utf-8'), self.load_base)
|
||||
self.classifier = classification_func
|
||||
|
||||
if self.handle == 0:
|
||||
print('Warning: Could not open', line)
|
||||
raise
|
||||
|
||||
def release(self):
|
||||
attplugin.deleteService(self.handle)
|
||||
|
||||
def inrange(self, addr):
|
||||
return addr >= self.load_base and addr < self.load_end+0x1000
|
||||
|
||||
def GetInstruction(self, addr):
|
||||
inst = attplugin.getInstruction(self.handle, addr)
|
||||
if inst is None:
|
||||
return None
|
||||
inst = inst.decode()
|
||||
while len(inst) and (inst[0] == '\t' or inst[0] == ' '):
|
||||
inst = inst[1:]
|
||||
while len(inst) and (inst[-1] == '\t' or inst[-1] == ' '):
|
||||
inst = inst[:-1]
|
||||
cpp = attplugin.getCppref(self.handle, addr)
|
||||
if cpp:
|
||||
cpp = cpp.decode()
|
||||
size = attplugin.getInstSize(self.handle, addr)
|
||||
if size and inst:
|
||||
return (self.classifier(inst.encode('utf-8'), len(inst)), inst, cpp, size)
|
||||
return None
|
||||
|
||||
|
||||
class CodeobjService:
|
||||
def __init__(self, att_kernel_txt, cfunc) -> None:
|
||||
cfunc.restype = ctypes.c_int
|
||||
cfunc.argtypes = [ctypes.c_char_p, ctypes.c_size_t]
|
||||
|
||||
self.last_instance = None
|
||||
self.services = []
|
||||
for line in att_kernel_txt:
|
||||
try:
|
||||
if 'memory://' == line[0:len('memory://')]:
|
||||
continue
|
||||
self.services.append(CodeobjInstance(line, cfunc))
|
||||
except:
|
||||
pass
|
||||
|
||||
def release(self):
|
||||
for _, _, instance in self.services:
|
||||
instance.release()
|
||||
|
||||
def GetInstruction(self, addr):
|
||||
if self.last_instance and self.last_instance.inrange(addr):
|
||||
return self.last_instance.GetInstruction(addr)
|
||||
|
||||
for instance in self.services:
|
||||
if instance.inrange(addr):
|
||||
self.last_instance = instance
|
||||
return instance.GetInstruction(addr)
|
||||
|
||||
return None
|
||||
|
||||
def getSymbolName(self, addr):
|
||||
name = attplugin.getSymbolName(addr)
|
||||
if name:
|
||||
return name.decode()
|
||||
return "Addr #"+hex(addr)
|
||||
@@ -53,12 +53,25 @@ WaveInstCategory = {
|
||||
|
||||
# Keeps track of register states for hipcc-generated assembly
|
||||
class RegisterWatchList:
|
||||
def __init__(self, labels):
|
||||
def __init__(self, labels, code, jump_map, insts):
|
||||
self.registers = {"v" + str(k): [[] for m in range(64)] for k in range(64)}
|
||||
for k in range(128):
|
||||
self.registers["s" + str(k)] = []
|
||||
self.labels = labels
|
||||
|
||||
self.code = code
|
||||
self.jump_map = jump_map
|
||||
self.insts = insts
|
||||
|
||||
def jump(self, as_line):
|
||||
return self.jump_map[as_line[2]]
|
||||
|
||||
def getcode(self, line):
|
||||
return self.code[line], 1
|
||||
|
||||
def getincrement(self, line):
|
||||
return 1
|
||||
|
||||
def try_translate(self, tok):
|
||||
if tok[0] in ["s"]:
|
||||
return self.registers[self.range(tok)[0]]
|
||||
@@ -153,12 +166,64 @@ class RegisterWatchList:
|
||||
except:
|
||||
pass
|
||||
|
||||
# Matches tokens in reverse order
|
||||
def try_match_swapped(self, i, line, increment):
|
||||
return self.insts[i + 1].type == self.code[line][1] and self.insts[i].type == self.code[line + 1][1]
|
||||
|
||||
# Translates PC values to instructions, for auto captured ISA
|
||||
class PCTranslator:
|
||||
def __init__(self, code, insts):
|
||||
def __init__(self, insts, code, raw_code, reverse_map, codeservice):
|
||||
self.codeservice = codeservice
|
||||
|
||||
self.insts = insts
|
||||
self.addrmap = {c[-3] : (c, self.codeservice.GetInstruction(c[-3])[3]) for c in code if c[-3] > 0}
|
||||
|
||||
self.code = code
|
||||
self.insts = insts[1:]
|
||||
self.addrmap = {code[m][-3] : m for m in range(len(code))}
|
||||
self.raw_code = raw_code
|
||||
self.reverse_map = reverse_map
|
||||
self.jump_map = {c[-3] : self.getjump_loc(c) for c in code if c[1] == BRANCH}
|
||||
|
||||
def jump(self, as_line):
|
||||
return self.jump_map[as_line[-3]]
|
||||
|
||||
def getcode(self, addr):
|
||||
try:
|
||||
return self.addrmap[addr]
|
||||
except Exception as ex:
|
||||
new_inst = self.codeservice.GetInstruction(addr)
|
||||
if new_inst and new_inst[3]: # Check returned size > 0
|
||||
last_line = self.raw_code[-1]
|
||||
newline = [new_inst[1], new_inst[0], len(self.raw_code), new_inst[2], last_line[4]+1, last_line[5]+1, addr, 0, 0]
|
||||
if new_inst[0] == BRANCH:
|
||||
self.jump_map[addr] = self.getjump_loc(newline)
|
||||
self.addrmap[addr] = (newline, new_inst[3])
|
||||
|
||||
next = len(self.code)
|
||||
self.reverse_map[addr] = len(self.raw_code)
|
||||
self.raw_code.append(newline)
|
||||
self.code.append(newline)
|
||||
return newline, new_inst[3]
|
||||
else:
|
||||
raise ex
|
||||
|
||||
def jump(self, asm_line):
|
||||
try:
|
||||
return self.jump_map[asm_line[-3]]
|
||||
except:
|
||||
loc = self.getjump_loc(asm_line)
|
||||
self.jump_map[asm_line[-3]] = loc
|
||||
return loc
|
||||
|
||||
def getjump_loc(self, asm_line):
|
||||
try:
|
||||
dest = int(asm_line[0].split(' ')[-1])
|
||||
if dest >= 32768: dest -= 65536
|
||||
return asm_line[-3] + 4*dest+4
|
||||
except:
|
||||
return -1
|
||||
|
||||
def getincrement(self, addr):
|
||||
return self.getcode(addr)[1]
|
||||
|
||||
def try_translate(self, tok):
|
||||
pass
|
||||
@@ -170,15 +235,13 @@ class PCTranslator:
|
||||
pass
|
||||
def swappc(self, line, line_num, inst_index):
|
||||
try:
|
||||
loc = self.addrmap[self.insts[inst_index+1].cycles]
|
||||
return loc
|
||||
return self.getcode(self.insts[inst_index+1].cycles)[0][-3]
|
||||
except:
|
||||
print('SWAPPC warning: Could not find addr', hex(self.insts[inst_index+1].cycles), 'for', inst_index, line)
|
||||
return -1
|
||||
def setpc(self, line, inst_index):
|
||||
try:
|
||||
loc = self.addrmap[self.insts[inst_index+1].cycles]
|
||||
return loc
|
||||
return self.getcode(self.insts[inst_index+1].cycles)[0][-3]
|
||||
except:
|
||||
print('SETPC warning: Could not find addr', hex(self.insts[inst_index+1].cycles), 'for', inst_index, line)
|
||||
return -1
|
||||
@@ -189,15 +252,19 @@ class PCTranslator:
|
||||
def updatelane(self, line):
|
||||
pass
|
||||
|
||||
# Matches tokens in reverse order
|
||||
def try_match_swapped(insts, code, i, line):
|
||||
return insts[i + 1].type == code[line][1] and insts[i].type == code[line + 1][1]
|
||||
# Matches tokens in reverse order
|
||||
def try_match_swapped(self, i, addr, increment):
|
||||
try:
|
||||
return self.insts[i + 1].type == self.getcode(addr)[0][1] and \
|
||||
self.insts[i].type == self.getcode(addr + increment)[0][1]
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
|
||||
def stitch(insts, raw_code, jumps, gfxv, bIsAuto):
|
||||
def stitch(insts, raw_code, jumps, gfxv, bIsAuto, codeservice):
|
||||
bGFX9 = gfxv == 'vega'
|
||||
|
||||
result, i, line, loopCount = [], 0, 0, defaultdict(int)
|
||||
result, i, loopCount = [], 0, defaultdict(int)
|
||||
|
||||
SMEM_INST = [] # scalar memory
|
||||
VLMEM_INST = [] # vector memory load
|
||||
@@ -228,8 +295,15 @@ def stitch(insts, raw_code, jumps, gfxv, bIsAuto):
|
||||
labels[c[0].split(":")[0]] = len(code)
|
||||
|
||||
reverse_map = {}
|
||||
for k, v in enumerate(jump_map):
|
||||
reverse_map[v] = k
|
||||
if bIsAuto:
|
||||
for k, v in enumerate(jump_map):
|
||||
try:
|
||||
reverse_map[code[v][-3]] = k
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
for k, v in enumerate(jump_map):
|
||||
reverse_map[v] = k
|
||||
|
||||
jumps = {jump_map[j] + 1: j for j in jumps}
|
||||
|
||||
@@ -242,42 +316,51 @@ def stitch(insts, raw_code, jumps, gfxv, bIsAuto):
|
||||
loops = 0
|
||||
maxline = 0
|
||||
|
||||
pcskip = []
|
||||
if bIsAuto:
|
||||
try:
|
||||
if insts[0].type != PCINFO:
|
||||
firstinst = insts[0]
|
||||
insts = insts[1:]
|
||||
|
||||
if firstinst.type != PCINFO:
|
||||
print('Warning: Waves without PCINFO')
|
||||
return None
|
||||
elif insts[0].cycles == 0:
|
||||
elif firstinst.cycles == 0:
|
||||
print('Info: Some waves started before the trace')
|
||||
return None
|
||||
watchlist = PCTranslator(code, insts)
|
||||
line = watchlist.addrmap[insts[0].cycles]
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
watchlist = PCTranslator(insts, code, raw_code, reverse_map, codeservice)
|
||||
line = firstinst.cycles
|
||||
lineincrement = watchlist.getincrement(line)
|
||||
pcskip.append(0)
|
||||
except KeyError as e:
|
||||
print('Auto error invalid addr', hex(e.args[0]))
|
||||
return None
|
||||
except Exception as e:
|
||||
print('Auto error', e)
|
||||
return None
|
||||
insts = insts[1:]
|
||||
else:
|
||||
watchlist = RegisterWatchList(labels=labels)
|
||||
line = 0
|
||||
lineincrement = 1
|
||||
watchlist = RegisterWatchList(labels=labels, code=code, jump_map=jump_map)
|
||||
|
||||
N = len(insts)
|
||||
|
||||
pcskip = []
|
||||
while i < N:
|
||||
while i < N and line >= 0 and loops < MAX_STITCHED_TOKENS:
|
||||
if insts[i].type == PCINFO:
|
||||
i += 1
|
||||
pcskip.append(i)
|
||||
continue
|
||||
|
||||
loops += 1
|
||||
if line >= len(code) or loops > MAX_STITCHED_TOKENS \
|
||||
or num_failed_stitches > MAX_FAILED_STITCHES:
|
||||
|
||||
inst = insts[i]
|
||||
try:
|
||||
as_line, lineincrement = watchlist.getcode(line)
|
||||
except:
|
||||
break
|
||||
|
||||
maxline = max(reverse_map[line], maxline)
|
||||
inst = insts[i]
|
||||
as_line = code[line]
|
||||
|
||||
matched = True
|
||||
next = line + 1
|
||||
next = line + lineincrement
|
||||
|
||||
if not bIsAuto:
|
||||
if '_mov_' in as_line[0]:
|
||||
@@ -287,7 +370,7 @@ def stitch(insts, raw_code, jumps, gfxv, bIsAuto):
|
||||
|
||||
if as_line[1] == GETPC:
|
||||
try:
|
||||
watchlist.getpc(as_line[0], code[line+1][0])
|
||||
watchlist.getpc(as_line[0], watchlist.getcode(next)[0])
|
||||
matched = inst.type in [SALU, JUMP]
|
||||
except:
|
||||
matched = False
|
||||
@@ -298,31 +381,31 @@ def stitch(insts, raw_code, jumps, gfxv, bIsAuto):
|
||||
next = watchlist.setpc(as_line[0], i)
|
||||
matched = inst.type in [SALU, JUMP]
|
||||
if bIsAuto:
|
||||
pcskip.append(i)
|
||||
i += 1
|
||||
pcskip.append(i+1)
|
||||
while next < 0 and i+1 < len(insts):
|
||||
if insts[i+1].type == PCINFO:
|
||||
next = watchlist.setpc(as_line[0], i)
|
||||
pcskip.append(i)
|
||||
else:
|
||||
inst.cycles += insts[i+1].cycles
|
||||
i += 1
|
||||
if insts[i].type == PCINFO:
|
||||
next = watchlist.setpc(as_line[0], i-1)
|
||||
pcskip.append(i+1)
|
||||
else:
|
||||
inst.cycles += insts[i].cycles
|
||||
if next < 0:
|
||||
print('Jump to unknown location in line', as_line[0])
|
||||
break
|
||||
elif as_line[1] == SWAPPC:
|
||||
next = watchlist.swappc(as_line[0], line, i)
|
||||
matched = inst.type in [SALU, JUMP]
|
||||
next = watchlist.swappc(as_line[0], line, i)
|
||||
if bIsAuto:
|
||||
pcskip.append(i)
|
||||
i += 1
|
||||
pcskip.append(i+1)
|
||||
while next < 0 and i+1 < len(insts):
|
||||
if insts[i+1].type == PCINFO:
|
||||
next = watchlist.swappc(as_line[0], line, i)
|
||||
pcskip.append(i)
|
||||
else:
|
||||
inst.cycles += insts[i+1].cycles
|
||||
i += 1
|
||||
if insts[i].type == PCINFO:
|
||||
next = watchlist.swappc(as_line[0], line, i-1)
|
||||
pcskip.append(i+1)
|
||||
else:
|
||||
inst.cycles += insts[i].cycles
|
||||
if next < 0:
|
||||
print('Jump to unknown location in line', as_line[0])
|
||||
break
|
||||
@@ -421,28 +504,26 @@ def stitch(insts, raw_code, jumps, gfxv, bIsAuto):
|
||||
num_inflight = NUM_FLAT + NUM_SMEM + NUM_VLMEM + NUM_VSMEM
|
||||
|
||||
elif inst.type == JUMP and as_line[1] == BRANCH:
|
||||
next = jump_map[as_line[2]]
|
||||
next = watchlist.jump(as_line)
|
||||
if next is None or next == 0:
|
||||
print("Jump to unknown location!", as_line)
|
||||
break
|
||||
elif inst.type == NEXT and as_line[1] == BRANCH:
|
||||
next = line + 1
|
||||
pass
|
||||
else:
|
||||
matched = False
|
||||
next = line + 1
|
||||
if i + 1 < N and line + 1 < len(code):
|
||||
if try_match_swapped(insts, code, i, line):
|
||||
temp = insts[i]
|
||||
insts[i] = insts[i + 1]
|
||||
insts[i + 1] = temp
|
||||
next = line
|
||||
elif "s_waitcnt " in as_line[0] or "_load_" in as_line[0]:
|
||||
if skipped_immed > 0 and "s_waitcnt " in as_line[0]:
|
||||
matched = True
|
||||
skipped_immed -= 1
|
||||
elif 'scratch_' not in as_line[0]:
|
||||
print('Parsing terminated at:', as_line)
|
||||
break
|
||||
if watchlist.try_match_swapped(i, line, lineincrement):
|
||||
temp = insts[i]
|
||||
insts[i] = insts[i + 1]
|
||||
insts[i + 1] = temp
|
||||
next = line
|
||||
elif "s_waitcnt " in as_line[0] or "_load_" in as_line[0]:
|
||||
if skipped_immed > 0 and "s_waitcnt " in as_line[0]:
|
||||
matched = True
|
||||
skipped_immed -= 1
|
||||
elif 'scratch_' not in as_line[0]:
|
||||
print('Parsing terminated at:', as_line)
|
||||
break
|
||||
|
||||
if matched:
|
||||
inst.asmline = reverse_map[line]
|
||||
@@ -457,6 +538,8 @@ def stitch(insts, raw_code, jumps, gfxv, bIsAuto):
|
||||
i += 1
|
||||
else:
|
||||
num_failed_stitches += 1
|
||||
|
||||
maxline = max(reverse_map[line], maxline)
|
||||
line = next
|
||||
|
||||
N = max(N, 1)
|
||||
@@ -475,5 +558,6 @@ def stitch(insts, raw_code, jumps, gfxv, bIsAuto):
|
||||
)
|
||||
break
|
||||
line += 1
|
||||
print('Sucessfuly parsed', i, 'tokens')
|
||||
|
||||
return result, loopCount, mem_unroll, flight_count, maxline, len(result), pcskip
|
||||
|
||||
@@ -3,5 +3,11 @@ global: rocprofiler_plugin_initialize;
|
||||
rocprofiler_plugin_finalize;
|
||||
rocprofiler_plugin_write_buffer_records;
|
||||
rocprofiler_plugin_write_record;
|
||||
createService;
|
||||
deleteService;
|
||||
getInstruction;
|
||||
getCppref;
|
||||
getInstSize;
|
||||
getSymbolName;
|
||||
local: *;
|
||||
};
|
||||
Reference in New Issue
Block a user