SWDEV-432445: ATT continuous mode

Change-Id: I52732fc1dba41df3373ef6a19a428b00d729bf04


[ROCm/rocprofiler commit: aba6a1d986]
This commit is contained in:
Giovanni LB
2023-11-15 12:18:51 -03:00
parent 96510269a5
commit 34fd4840d1
29 changed files with 1841 additions and 851 deletions
+41 -56
View File
@@ -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;
+32 -21
View File
@@ -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,
+192 -16
View File
@@ -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;
};
+48 -39
View File
@@ -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)
+147 -63
View File
@@ -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
+6
View File
@@ -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: *;
};