SWDEV-432445: Handling multiple agents for ATT

Change-Id: I41d30baf2c469d8ddb58e3385b88a7b04a30422e
This commit is contained in:
Giovanni LB
2023-12-05 23:25:15 -03:00
committed by Giovanni Baraldi
parent edf93d48ab
commit b93d5e7e79
9 changed files with 155 additions and 238 deletions
+22 -12
View File
@@ -438,21 +438,21 @@ if __name__ == "__main__":
"--mode",
help="""ATT analysis modes:\n
off: Only run ATT collection, disable analysis.\n
file: dump json files to disk.\n
network: Open att server over the network.""",
file: dump json files to disk.""",
type=str,
default="off",
)
args = parser.parse_args()
args.mode = args.mode.lower().split(',')
CSV_MODE = False
if args.mode.lower() == 'csv':
FILE_MODE = False
if 'csv' in args.mode:
CSV_MODE = True
elif args.mode.lower() == 'file':
args.dumpfiles = True
elif args.mode.lower() == "network":
args.dumpfiles = False
else:
if 'file' in args.mode:
FILE_MODE = True
if not CSV_MODE and not FILE_MODE:
print("Skipping analysis.")
quit()
@@ -482,10 +482,21 @@ if __name__ == "__main__":
for line in f:
att_kernel_f.append(line.split('\n')[0])
# returns element index of the file:// or memory:// filepath in the string
get_path_loc = lambda x: np.sum([len(m) for m in x.split(' ')[:3]])+3
# adds the OUTPUT_PATH env variable to a filepath if necessary
add_pathnv = lambda x: x[:get_path_loc(x)] + os.path.join(pathenv, x[get_path_loc(x):])
# returns the memory address in the string
get_addr = lambda x: int(x.split(' ')[0][2:], 16)
# Sets a preference for 'file' paths to be added before 'memory' paths. Sorts by addr.
get_addr_preference = lambda x: [0 if 'file' in x[get_path_loc(x):] else 1<<60][0]
# Get the GPU id in the string
gpu_id = int(att_kernel_f[0].split(' ')[2].split('GPU[')[1].split(']')[0])
# Eliminame first line in the att_kernel txt file and adds the OUTPUT_PATH as needed
att_kernel_f = [add_pathnv(p) if '.out' == p[-4:] else p for p in att_kernel_f[1:]]
# Sorts the list of codeobj by address, with 'file' given preference
att_kernel_f = sorted(att_kernel_f, key=lambda x: get_addr(x)+get_addr_preference(x))
assembly_code = deepcopy(args.assembly_code)
# Assembly parsing
@@ -526,7 +537,7 @@ if __name__ == "__main__":
gc.collect()
if bIsAuto:
codeservice = CodeobjService(att_kernel_f, SO.classify_asm_line)
codeservice = CodeobjService(gpu_id, att_kernel_f, SO.classify_asm_line)
else:
codeservice = None
@@ -563,7 +574,8 @@ if __name__ == "__main__":
if CSV_MODE:
from att_to_csv import dump_csv
dump_csv(code, trace_instance_name)
else:
if FILE_MODE:
drawinfo = {
"TIMELINES": gen_timelines(DBFILES),
"EVENTS": EVENTS,
@@ -573,11 +585,9 @@ if __name__ == "__main__":
"DispatchNames": {id: codeservice.getSymbolName(addr) for id, addr in kernel_addr.items()}
}
view_trace(
args,
code,
DBFILES,
analysed_filenames,
args.dumpfiles,
0,
gfxv,
drawinfo,
+50 -35
View File
@@ -53,24 +53,27 @@
#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; \
#define C_API_END(returndata) \
} catch (std::exception& e) \
{ \
std::cerr << "Codeobj API lookup error: " << e.what() << std::endl; \
return returndata; \
} \
catch (std::string& s) \
{ \
std::cerr << "Codeobj API lookup error: " << s << std::endl; \
return returndata; \
} \
catch (...) \
{ \
return returndata; \
}
CodeObjDecoderComponent::CodeObjDecoderComponent(const char* codeobj_data, uint64_t codeobj_size)
{
CodeObjDecoderComponent::CodeObjDecoderComponent(
const char* codeobj_data,
uint64_t codeobj_size,
uint64_t gpu_id
) {
m_fd = -1;
#if defined(_GNU_SOURCE) && defined(MFD_ALLOW_SEALING) && defined(MFD_CLOEXEC)
m_fd = ::memfd_create(m_uri.c_str(), MFD_ALLOW_SEALING | MFD_CLOEXEC);
@@ -113,21 +116,28 @@ CodeObjDecoderComponent::CodeObjDecoderComponent(const char* codeobj_data, uint6
size_t line_count;
if (dwarf_getsrclines(&die, &lines, &line_count)) continue;
std::shared_ptr<std::string> dwarf_line_number{nullptr};
for (size_t i = 0; i < line_count; ++i) {
Dwarf_Addr addr;
int line_number;
Dwarf_Line* line = dwarf_onesrcline(lines, i);
if (!line) continue;
if (!dwarf_lineaddr(line, &addr) && !dwarf_lineno(line, &line_number) && line_number)
m_line_number_map[addr] = {dwarf_linesrc(line, nullptr, nullptr), line_number};
if (line && !dwarf_lineaddr(line, &addr) && !dwarf_lineno(line, &line_number) && line_number)
{
std::string src = dwarf_linesrc(line, nullptr, nullptr);
dwarf_line_number = std::make_shared<std::string>(src + ':' + std::to_string(line_number));
}
if (dwarf_line_number.get())
m_line_number_map[addr] = dwarf_line_number;
}
cu_offset = next_offset;
}
// load_symbol_map();
}
disassembly = std::make_unique<DisassemblyInstance>(codeobj_data, codeobj_size, std::nullopt); // Can throw
// Can throw
disassembly = std::make_unique<DisassemblyInstance>(codeobj_data, codeobj_size, gpu_id);
try {
m_symbol_map = disassembly->GetKernelMap(); // Can throw
} catch(...) {}
@@ -169,15 +179,11 @@ CodeObjDecoderComponent::disassemble_instruction(uint64_t faddr, uint64_t vaddr)
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));
}
auto it = m_line_number_map.find(vaddr);
if (it != m_line_number_map.end())
cpp_line = it->second->data();
size_t size = disassembly->ReadInstruction(faddr, vaddr, cpp_line);
return {disassembly->last_instruction, size};
}
@@ -217,8 +223,12 @@ void CodeObjDecoderComponent::disassemble_single_kernel(uint64_t kaddr) {
disassemble_kernel(v.faddr, vaddr);
}
CodeobjDecoder::CodeobjDecoder(const char* filepath, uint64_t loadbase, uint64_t mem_size):
loadbase(loadbase), load_end(loadbase + mem_size)
CodeobjDecoder::CodeobjDecoder(
const char* filepath,
uint64_t loadbase,
uint64_t mem_size,
uint64_t gpu_id
): loadbase(loadbase), load_end(loadbase + mem_size)
{
if (!filepath) throw "Empty filepath.";
@@ -237,13 +247,13 @@ CodeobjDecoder::CodeobjDecoder(const char* filepath, uint64_t loadbase, uint64_t
file.seekg(0, file.beg);
file.read(buffer.data(), buffer.size());
decoder = std::make_unique<CodeObjDecoderComponent>(buffer.data(), buffer.size());
decoder = std::make_unique<CodeObjDecoderComponent>(buffer.data(), buffer.size(), gpu_id);
}
else
{
std::unique_ptr<CodeObjectBinary> binary = std::make_unique<CodeObjectBinary>(filepath);
auto& buffer = binary->buffer;
decoder = std::make_unique<CodeObjDecoderComponent>(buffer.data(), buffer.size());
decoder = std::make_unique<CodeObjDecoderComponent>(buffer.data(), buffer.size(), gpu_id);
}
auto elf_segments = decoder->disassembly->getSegments();
@@ -297,11 +307,16 @@ CodeobjTableTranslation table;
extern "C"
{
PUBLIC_API int addDecoder(const char* filename, uint32_t id, uint64_t loadbase, uint64_t memsize)
{
PUBLIC_API int addDecoder(
const char* filename,
uint32_t id,
uint64_t loadbase,
uint64_t memsize,
uint64_t gpu_id
) {
C_API_BEGIN
table.addDecoder(filename, id, loadbase, memsize);
table.addDecoder(filename, id, loadbase, memsize, gpu_id);
return 0;
C_API_END(1)
+20 -7
View File
@@ -37,7 +37,7 @@ class CodeObjDecoderComponent
public:
std::optional<SymbolInfo> find_symbol(uint64_t address);
CodeObjDecoderComponent(const char* codeobj_data, uint64_t codeobj_size);
CodeObjDecoderComponent(const char* codeobj_data, uint64_t codeobj_size, uint64_t gpu_id);
~CodeObjDecoderComponent();
std::pair<instruction_instance_t, size_t>
@@ -48,7 +48,7 @@ public:
int m_fd;
std::map<uint64_t, std::pair<std::string, size_t>> m_line_number_map{};
std::map<uint64_t, std::shared_ptr<std::string>> m_line_number_map{};
std::map<uint64_t, SymbolInfo> m_symbol_map{};
std::string m_uri;
@@ -65,7 +65,8 @@ typedef struct {
class CodeobjDecoder
{
public:
CodeobjDecoder(const char* filepath, uint64_t loadbase, uint64_t memsize);
CodeobjDecoder(const char* filepath, uint64_t loadbase, uint64_t memsize, uint64_t gpu_id);
bool decode_single(uint64_t vaddr);
bool decode_single_at_offset(uint64_t vaddr, uint64_t voffset);
bool add_to_map(uint64_t faddr, uint64_t vaddr, uint64_t voffset);
@@ -111,9 +112,15 @@ class CodeobjList
public:
CodeobjList() = default;
virtual void addDecoder(const char* filepath, uint32_t id, uint64_t loadbase, uint64_t memsize)
virtual void addDecoder(
const char* filepath,
uint32_t id,
uint64_t loadbase,
uint64_t memsize,
uint64_t gpu_id
)
{
decoders[id] = std::make_shared<CodeobjDecoder>(filepath, loadbase, memsize);
decoders[id] = std::make_shared<CodeobjDecoder>(filepath, loadbase, memsize, gpu_id);
}
virtual bool removeDecoder(uint32_t id)
@@ -150,9 +157,15 @@ class CodeobjTableTranslation : protected CodeobjList
public:
CodeobjTableTranslation() = default;
void addDecoder(const char* filepath, uint32_t id, uint64_t loadbase, uint64_t memsize) override
virtual void addDecoder(
const char* filepath,
uint32_t id,
uint64_t loadbase,
uint64_t memsize,
uint64_t gpu_id
) override
{
this->Super::addDecoder(filepath, id, loadbase, memsize);
this->Super::addDecoder(filepath, id, loadbase, memsize, gpu_id);
auto ptr = decoders.at(id);
table.insert({ptr->begin(), static_cast<uint32_t>(ptr->size()), id, 0});
}
+13 -9
View File
@@ -70,6 +70,8 @@ if (amd_comgr_status_s status = call) {
return AMD_COMGR_STATUS_ERROR; \
}
std::unordered_map<uint64_t, std::string> DisassemblyInstance::agent_isa_name{};
CodeObjectBinary::CodeObjectBinary(const std::string& uri) : m_uri(uri) {
const std::string protocol_delim{"://"};
@@ -156,7 +158,7 @@ CodeObjectBinary::CodeObjectBinary(const std::string& uri) : m_uri(uri) {
DisassemblyInstance::DisassemblyInstance(
const char* codeobj_data,
uint64_t codeobj_size,
std::optional<std::string> input_isa
uint64_t gpu_id
)
{
buffer = std::vector<char>(codeobj_size, 0);
@@ -164,20 +166,22 @@ DisassemblyInstance::DisassemblyInstance(
THROW_COMGR(amd_comgr_create_data(AMD_COMGR_DATA_KIND_EXECUTABLE, &data));
THROW_COMGR(amd_comgr_set_data(data, buffer.size(), buffer.data()));
if (!input_isa)
std::string input_isa{};
if (agent_isa_name.find(gpu_id) == agent_isa_name.end())
{
input_isa = "amdgcn-amd-amdhsa--gfx1100";
size_t isa_size = 128;
input_isa.resize(isa_size);
THROW_COMGR(amd_comgr_get_data_isa_name(data, &isa_size, input_isa.data()));
agent_isa_name[gpu_id] = input_isa;
}
if (!input_isa)
else
{
input_isa = std::string();
input_isa->resize(128);
size_t isa_size = sizeof(input_isa->size());
THROW_COMGR(amd_comgr_get_data_isa_name(data, &isa_size, input_isa->data()));
input_isa = agent_isa_name.at(gpu_id);
}
THROW_COMGR(amd_comgr_create_disassembly_info(
input_isa->data(),
input_isa.data(),
&DisassemblyInstance::memory_callback, &DisassemblyInstance::inst_callback,
[](uint64_t address, void* user_data) {}, &info));
+3 -1
View File
@@ -51,7 +51,7 @@ class DisassemblyInstance {
DisassemblyInstance(
const char* codeobj_data,
uint64_t codeobj_size,
std::optional<std::string> input_isa
uint64_t gpu_id
);
~DisassemblyInstance();
@@ -61,6 +61,8 @@ class DisassemblyInstance {
static uint64_t memory_callback(uint64_t from, char* to, uint64_t size, void* user_data);
static void inst_callback(const char* instruction, void* user_data);
static amd_comgr_status_t symbol_callback(amd_comgr_symbol_t symbol, void* user_data);
// Per-gpu_id isa_name
static std::unordered_map<uint64_t, std::string> agent_isa_name;
std::optional<uint64_t> va2fo(uint64_t va);
std::vector<std::pair<uint64_t, uint64_t>> getSegments();
+13 -10
View File
@@ -33,7 +33,7 @@ attplugin.getInstructionFromID.restype = instruction_info_t
attplugin.getInstructionFromID.argtypes = [c_uint32, c_uint64]
attplugin.addDecoder.restype = c_int
attplugin.addDecoder.argtypes = [c_char_p, c_uint32, c_uint64, c_uint64]
attplugin.addDecoder.argtypes = [c_char_p, c_uint32, c_uint64, c_uint64, c_uint64]
attplugin.removeDecoder.restype = c_int
attplugin.removeDecoder.argtypes = [c_uint32, c_uint64]
@@ -48,15 +48,15 @@ def getOffset(addr):
return addr & OFFSET_MASK
class CodeobjInstance:
def __init__(self, line):
def __init__(self, gpu_id, line):
tokens = line.split(' ')
self.load_base = int(tokens[0], 16)
self.memsize = int(tokens[1], 16)
self.att_id = int(tokens[2])
self.fpath = tokens[3]
encoded = self.fpath.encode('utf-8')
self.error = attplugin.addDecoder(encoded, self.att_id, self.load_base, self.memsize)
path = self.fpath.encode('utf-8')
self.error = attplugin.addDecoder(path, self.att_id, self.load_base, self.memsize, gpu_id)
if self.error != 0:
print('Warning: Could not open', line)
raise
@@ -66,7 +66,7 @@ class CodeobjInstance:
class CodeobjService:
def __init__(self, att_kernel_txt, cfunc):
def __init__(self, gpu_id, att_kernel_txt, cfunc):
cfunc.restype = ctypes.c_int
cfunc.argtypes = [ctypes.c_char_p, ctypes.c_size_t]
@@ -77,7 +77,7 @@ class CodeobjService:
try:
if 'memory://' == line[0:len('memory://')]:
continue
service = CodeobjInstance(line)
service = CodeobjInstance(gpu_id, line)
self.services[service.att_id] = service
except:
pass
@@ -132,7 +132,10 @@ class CodeobjService:
return (self.classifier(info_inst.inst, len(inst)), inst, cpp, info_inst.size)
def getSymbolName(self, addr):
name = attplugin.getSymbolName(self.ToRawPC(addr))
if name:
return name.decode()
return "Addr #"+hex(self.ToRawPC(addr))
try:
name = attplugin.getSymbolName(self.ToRawPC(addr))
if name:
return name.decode()
return "Addr #"+hex(self.ToRawPC(addr))
except:
return "Addr #"+hex(addr)
+10 -150
View File
@@ -153,127 +153,6 @@ def extract_data(df, se_number):
return flight_count, wave_filenames, se_filename, allwaves_maxline
class NoCacheHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
self.send_my_headers()
http.server.SimpleHTTPRequestHandler.end_headers(self)
def send_my_headers(self):
self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
self.send_header("Pragma", "no-cache")
self.send_header("Expires", "0")
def do_GET(self):
if (
".png?" in self.path
and self.path.split("/")[-1] not in JSON_GLOBAL_DICTIONARY.keys()
):
selections = [int(s) != 0 for s in self.path.split(".png?")[-1]]
counters_json, imagebytes = GeneratePIC(
self.drawinfo, selections[1:], selections[0]
)
JSON_GLOBAL_DICTIONARY["graph_options.json"] = counters_json
JSON_GLOBAL_DICTIONARY[self.path.split("/")[-1]] = imagebytes[
self.path.split("/")[-1].split("?")[0]
]
if ".json" in self.path or ".png" in self.path:
try:
response_file = JSON_GLOBAL_DICTIONARY[self.path.split("/")[-1]]
except:
print("Invalid json request:", self.path)
print(JSON_GLOBAL_DICTIONARY.keys())
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
return
self.send_response(HTTPStatus.OK)
self.send_header("Content-Length", str(len(response_file)))
if ".b" in self.path:
self.send_header("Content-type", "application/octet-stream")
response_file = BytesIO(response_file)
elif "timeline.png" in self.path:
self.send_header("Content-type", "image/png")
else:
self.send_header("Content-type", "application/json")
self.send_header("Last-Modified", self.date_time_string(time.time()))
self.end_headers()
self.copyfile(response_file, self.wfile)
elif self.path in ["/", "/styles.css", "/index.html", "/logo.svg"]:
http.server.SimpleHTTPRequestHandler.do_GET(self)
else:
print("Invalid request:", self.path)
self.send_error(HTTPStatus.NOT_FOUND, "File not found")
class RocTCPServer(socketserver.TCPServer):
def server_bind(self):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
def run_server(drawinfo):
Handler = NoCacheHTTPRequestHandler
Handler.drawinfo = drawinfo
os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)),"ui/"))
try:
with RocTCPServer((IPAddr, PORT), Handler) as httpd:
httpd.serve_forever()
except KeyboardInterrupt:
pass
def fix_space(line):
line = line.replace(" ", SP)
line = line.replace("\t", SP * 4)
return line
def WebSocketserver(websocket, path):
data = websocket.recv()
cpp, ln, _ = data.split(":")
ln = int(ln)
HL, EMP = "highlight", ""
content = None
print("loading...")
try:
f = open(cpp, "r", errors="replace")
content = "".join(
'<li class="line_'
+ str(i)
+ str(HL if i == ln else EMP)
+ '">'
+ str(i).ljust(5)
+ fix_space(l)
+ "</li>"
for i, l in enumerate(f.readlines(), 1)
)
except FileNotFoundError:
content = cpp + " not found!"
websocket.send(content)
def run_websocket():
start_server = websockets.serve(WebSocketserver, IPAddr, WebSocketPort)
try:
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
except KeyboardInterrupt:
pass
def assign_ports(ports):
ps = [int(port) for port in ports.split(",")]
if ps[0] <= 5000 or ps[1] <= 5000:
print("Need to have port values > 5000")
sys.exit(1)
elif ps[0] == ps[1]:
print(
"Can not use the same port for both web server and websocket server: " + ps[0]
)
sys.exit(1)
global IPAddr, PORT, WebSocketPort
PORT, WebSocketPort = ps[0], ps[1]
def call_picture_callback(return_dict, drawinfo):
response, imagebytes = GeneratePIC(drawinfo)
return_dict["graph_options.json"] = response
@@ -291,11 +170,9 @@ def call_picture_callback(return_dict, drawinfo):
def view_trace(
args,
code,
dbnames,
att_filenames,
bDumpOnly,
se_time_begin,
gfxv,
drawinfo,
@@ -380,30 +257,13 @@ def view_trace(
for k, v in return_dict.items():
JSON_GLOBAL_DICTIONARY[k] = v
if bDumpOnly == False:
JSON_GLOBAL_DICTIONARY["live.json"] = Readable({"live": 1})
if args.ports:
assign_ports(args.ports)
print("serving at ports: {0},{1}".format(PORT, WebSocketPort))
try:
PROCS = [
Process(target=run_server, args=[drawinfo]),
Process(target=run_websocket),
]
for p in PROCS:
p.start()
for p in PROCS:
p.join()
except KeyboardInterrupt:
print("Exitting.")
else:
os.makedirs(trace_instance_name + "_ui/", exist_ok=True)
JSON_GLOBAL_DICTIONARY["live.json"] = Readable({"live": 0})
os.system(
"cp "
+ os.path.join(os.path.abspath(os.path.dirname(__file__)), "ui")
+ "/* " + trace_instance_name + "_ui/"
)
for k, v in JSON_GLOBAL_DICTIONARY.items():
with open(os.path.join(trace_instance_name+"_ui", k), "w" if ".json" in k else "wb") as f:
f.write(v.read())
os.makedirs(trace_instance_name + "_ui/", exist_ok=True)
JSON_GLOBAL_DICTIONARY["live.json"] = Readable({"live": 0})
os.system(
"cp "
+ os.path.join(os.path.abspath(os.path.dirname(__file__)), "ui")
+ "/* " + trace_instance_name + "_ui/"
)
for k, v in JSON_GLOBAL_DICTIONARY.items():
with open(os.path.join(trace_instance_name+"_ui", k), "w" if ".json" in k else "wb") as f:
f.write(v.read())