Files
foc/kernel/fiasco/tool/gdb/fiasco_gdb_util.py
2017-05-02 15:25:27 +02:00

414 lines
14 KiB
Python

# Some useful utilities to be used with Fiasco.OC
# by adam@l4re.org
#
import gdb
import re
import os
class Toff(gdb.Command):
print_all = False
def __init__(self):
super (Toff, self).__init__ ('fiasco-offsets', gdb.COMMAND_DATA)
def print_members(self, t, indent = 0):
for f in sorted(t.fields(), key=lambda x: getattr(x, 'bitpos', -1)):
if f.type.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
if hasattr(f, 'bitpos'):
self.print_type(f.type, indent + 1, f.name,
"/* %d+%d */" % (f.bitpos // 8, f.type.sizeof))
elif self.print_all:
self.print_type(f.type, indent + 1, f.name,
"/* %d */" % (f.type.sizeof))
elif hasattr(f, 'bitpos'):
print(' ' * indent + ' %s %s; /* %d+%d */' % (f.type, f.name,
f.bitpos // 8,
f.type.sizeof))
elif self.print_all:
print(' ' * indent + ' %s %s; /* %d */' % (f.type, f.name,
f.type.sizeof))
def print_type(self, t, indent = 0, sname = None, t_desc = ""):
if t.code in [gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
s = "struct" if t.code is gdb.TYPE_CODE_STRUCT else "union"
if t.name == None:
print(' ' * indent + "%s {" % (s))
else:
print(' ' * indent + "%s %s {" % (s, t.name))
self.print_members(t, indent)
if t.code in [ gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION]:
if sname is None:
print(' ' * indent + "}; %s" % (t_desc))
else:
print(' ' * indent + "} %s; %s" % (sname, t_desc))
def invoke(self, arg, from_tty):
argv = gdb.string_to_argv(arg)
if len(argv) < 1:
raise gdb.GdbError('fiasco-offsets needs a type.')
self.print_type(gdb.lookup_type(argv[0]))
Toff()
class Log_table(gdb.Command):
def __init__(self):
super (Log_table, self).__init__('fiasco-libftbuf-gen',
gdb.COMMAND_DATA, gdb.COMPLETE_FILENAME)
db = {}
def invoke(self, arg, from_tty):
argv = gdb.string_to_argv(arg)
if len(argv) < 1:
raise gdb.GdbError('fiasco-libftbuf-gen: Need at least one image file.')
for f in argv:
self.query_one(f)
self.write_out()
def query_one(self, file):
print("Processing %s" % (file));
gdb.execute("file %s" % (file))
log_table = gdb.execute("info address _log_table", False, True)
log_table_end = gdb.execute("info address _log_table_end", False, True)
# is there any more direct way of getting the addresses?
regexp = re.compile(' (is at|at address) (0x\w+)')
m_start = regexp.search(log_table)
m_end = regexp.search(log_table_end)
if not m_start or not m_end:
raise gdb.GdbError("Failed to get _log_table and/or _log_table_end")
log_table = int(m_start.group(2), 0)
log_table_end = int(m_end.group(2), 0)
log_table_entry = gdb.lookup_type("Tb_log_table_entry")
for e in range(log_table, log_table_end, log_table_entry.sizeof):
fullname = gdb.parse_and_eval("((Tb_log_table_entry *)%d)->name" % e)
# advance to next string go get the shortname
tag = fullname
while True:
v = gdb.parse_and_eval("*(char *)%d" % tag);
tag += 1
if v == 0:
break
tag_s = tag.string()
fn_s = fullname.string()
if hasattr(self.db, tag_s) and self.db[tag_s] != fn_s:
raise gdb.GdbError("Mismatch, should not happen (%s vs %s for %s)" % (
self.db[tag_s], fn_s, tag_s))
self.db[tag_s] = fn_s
def write_out(self):
libftbuf_base_dir = os.getenv("LIBFTBUF_BASE_DIR")
if not libftbuf_base_dir:
raise gdb.GdbError("Error: Need to set envvar LIBFTBUF_BASE_DIR")
f_header = libftbuf_base_dir + '/include/kernel_inc.h'
f_c = libftbuf_base_dir + '/lib/src/data_inc.c'
print("Writing", f_header)
with open(f_header, 'w') as f:
f.write("/* This code is auto-generated from Fiasco binaries */\n")
f.write("#pragma once\n")
f.write("\n")
f.write("#ifndef __LIBFTBUF_KERNEL_INC_OK\n")
f.write("#error Do not include this header directly, use l4/libftbuf/ftbuf.h.\n")
f.write("#endif\n")
f.write("\n")
f.write("#include <l4/sys/ktrace_events.h>\n")
f.write("\n")
f.write("struct libftbuf_tb_entries_dyn_names_t\n")
f.write("{\n")
f.write(" const char *name;\n")
f.write(" const char *tag;\n")
f.write("};\n")
f.write("\n")
f.write("extern struct libftbuf_tb_entries_dyn_names_t\n")
f.write(" libftbuf_tb_entries_dyn_names[];\n");
f.write("\n")
f.write("enum libftbuf_tb_entry_dyn_event\n")
f.write("{\n")
first = True
for k in sorted(self.db.keys()):
k = k.replace(" ", "_")
if first:
first = False
f.write(" Ftbuf_event_%s = l4_ktrace_tbuf_dynentries,\n" % (k))
else:
f.write(" Ftbuf_event_%s,\n" % (k))
f.write(" Ftbuf_event_max\n")
f.write("};\n")
print("Writing", f_c)
with open(f_c, 'w') as f:
f.write("/* This code is auto-generated from Fiasco binaries */\n")
f.write("\n")
f.write("#define __LIBFTBUF_KERNEL_INC_OK\n")
f.write("#include <l4/libftbuf/kernel_inc.h>\n")
f.write("\n")
f.write("struct libftbuf_tb_entries_dyn_names_t libftbuf_tb_entries_dyn_names[] =\n")
f.write("{\n")
for k in sorted(self.db.keys()):
f.write(" { \"%s\", \"%s\" },\n" % (self.db[k], k))
f.write("};\n")
Log_table()
class Fiasco_tbuf(gdb.Command):
base_block_size = 0
tb_entry_size = 0
typedefs = {}
ktrace_events_file = "ktrace_events.h"
ktrace_shortnames = {
"Context::Drq_log": "drq",
"Context::Vcpu_log": "vcpu",
"Factory::Log_entry": "factory",
"Ipc_gate::Log_ipc_gate_invoke": "gate",
"Irq_base::Irq_log": "irq",
"Kobject::Log_destroy": "destroy",
"Mu_log::Map_log": "map",
"Mu_log::Unmap_log": "unmap",
"Rcu::Log_rcu": "rcu",
"Task::Log_unmap": "tunmap",
"Tb_entry_bp" : "bp",
"Tb_entry_ctx_sw": "context_switch",
"Tb_entry_ipc": "ipc",
"Tb_entry_ipc_res": "ipc_res",
"Tb_entry_ipc_trace": "ipc_trace",
"Tb_entry_empty": "empty",
"Tb_entry_ke": "ke",
"Tb_entry_ke_bin": "ke_bin",
"Tb_entry_ke_reg": "ke_reg",
"Tb_entry_pf": "pf",
"Tb_entry_sched": "sched",
"Tb_entry_trap": "trap",
"Tb_entry_union": "fullsize",
"Thread::Log_exc_invalid": "ieh",
"Thread::Log_pf_invalid": "ipfh",
"Thread::Log_thread_exregs": "exregs",
"Thread::Migration_log": "migration",
"Timer_tick::Log": "timer",
"Vm_svm::Log_vm_svm_exit": "svm",
}
# Non-simple types
known_types_map = {
"Cap_index" : "unsigned long",
"Cpu_number" : "unsigned",
"Context::Drq_log::Type" : "unsigned",
"L4_msg_tag" : "unsigned long",
"L4_obj_ref" : "unsigned long",
"L4_timeout_pair" : "unsigned",
"L4_error" : "unsigned long",
"cxx::Type_info" : "unsigned long",
}
printlog_buf_current = 0
printlog_buf = [ "", "", "" ]
def __init__(self):
super (Fiasco_tbuf, self).__init__("fiasco-gen-ktrace-events",
gdb.COMMAND_DATA)
def printlog(self, s):
print(s, end=' ')
self.printlog_buf[self.printlog_buf_current] += s
def printlogi(self, indentlevel, s):
ins = ' ' * (indentlevel * 1)
print("%s%s" % (ins, s), end=' ')
self.printlog_buf[self.printlog_buf_current] += ins + s
def printlog_set_current_section(self, section):
self.printlog_buf_current = section
def printlog_write(self):
with open(self.ktrace_events_file, 'w') as f:
f.write(self.printlog_buf[0])
f.write(self.printlog_buf[1])
f.write(self.printlog_buf[2])
def convert_name_to_c(self, name):
return "L4_ktrace_t__" + name.replace("::", "__")
def handle_type_pointer(self, t):
rt = str(t)
if rt != "void" and rt != "char":
rt = self.convert_name_to_c(rt)
self.typedefs[rt] = "void"
return rt
def handle_type(self, t):
if t.name in self.known_types_map:
# check that our conversion map is right
t2 = gdb.lookup_type(self.known_types_map[t.name]);
if t.sizeof != t2.sizeof:
raise gdb.GdbError("%s(%d) -> %s(%d) is not valid." % (
t.name, t.sizeof, t2.name, t2.sizeof))
st__ = self.convert_name_to_c(t.name)
self.typedefs[st__] = self.known_types_map[t.name]
return st__
if t.name == "bool":
return "char"
rtbasic = str(gdb.types.get_basic_type(t))
if str(rtbasic) != t.name:
n = self.convert_name_to_c(t.name)
self.typedefs[n] = rtbasic
return n
return t.name
def print_members(self, t, prepad, postpad = False, indent = 2):
first = True
behind_last_member = 0
padidx = 1
cur_size = 0
for f in sorted(t.fields(), key=lambda x: getattr(x, 'bitpos', -1)):
if f.name == "Tb_entry":
continue
if hasattr(f, 'bitpos'):
byteoff = f.bitpos // 8
if byteoff * 8 != f.bitpos:
raise gdb.GdbError("Don't know how to handle bitfields, hack me")
if prepad:
prepad = False
if self.base_block_size != 0 and byteoff != self.base_block_size:
# Add padding
padding = byteoff - self.base_block_size
self.printlogi(indent, 'char __pre_pad[%d];\n' % padding)
elif cur_size < byteoff:
padding = byteoff - cur_size
self.printlogi(indent, 'char __pad_%d[%d];\n' % (padidx, padding))
padidx += 1
behind_last_member = byteoff + f.type.sizeof
if f.type.code == gdb.TYPE_CODE_ARRAY:
tc = self.handle_type(f.type.target().unqualified())
c = "%s %s[%d]" % (tc, f.name, f.type.range()[1] + 1)
self.printlogi(indent, '%s; /* %d+%d */\n' % (c, byteoff,
f.type.sizeof))
elif f.type.code == gdb.TYPE_CODE_PTR:
tc = self.handle_type_pointer(f.type.target().unqualified())
self.printlogi(indent, '%s *%s; /* %d+%d */\n'
% (tc, f.name, byteoff, f.type.sizeof))
elif (f.type.code in [gdb.TYPE_CODE_UNION, gdb.TYPE_CODE_STRUCT]
and str(f.type.unqualified()) not in self.known_types_map):
s = "struct" if f.type.code is gdb.TYPE_CODE_STRUCT else "union"
self.printlogi(indent, '%s __attribute__((__packed__)) {\n' % (s))
self.print_members(f.type, False, False, indent + 2)
self.printlogi(indent, '} %s; /* %d+%d */\n' % (
f.name, byteoff, f.type.sizeof))
else:
tc = self.handle_type(f.type.unqualified())
self.printlogi(indent, '%s %s; /* %d+%d */\n'
% (tc, f.name, byteoff, f.type.sizeof))
cur_size = byteoff + f.type.sizeof
if postpad:
if behind_last_member > self.tb_entry_size:
raise gdb.GdbError("Error: %s is too big (expected <= %d)" % (
t.name, self.tb_entry_size))
sz = self.tb_entry_size - behind_last_member
self.printlogi(indent, 'char __post_pad[%d]; /* %d+%d */\n'
% (sz, behind_last_member, sz))
return behind_last_member
def print_single_struct(self, t, sname):
self.printlogi(4, "struct __attribute__((__packed__))\n")
self.printlogi(4, "{\n")
self.print_members(t, True, sname == "fullsize", 6)
self.printlogi(4, "} %s; /* %d */\n" % (sname, t.sizeof))
def gen_ktrace_events(self, tbentry_types):
self.printlog("/* Note, automatically generated from Fiasco binary */\n")
self.printlog("#pragma once\n")
self.printlog("\n")
t = gdb.lookup_type("Tbuf_entry_fixed")
if t.code is gdb.TYPE_CODE_ENUM:
self.printlog("enum L4_ktrace_tbuf_entry_fixed\n")
self.printlog("{\n")
for f in t.fields():
self.printlog(" l4_ktrace_%s = %d,\n" % (str.lower(f.name),
f.enumval))
self.printlog("};\n")
else:
raise gdb.GdbError("Missing Tbuf_entry_fixed, old Fiasco?")
# Unfortunately we are not able to extract Tb_entry::Tb_entry_size
# so apply this guess:
mword = gdb.lookup_type("Mword")
self.tb_entry_size = 128 if mword.sizeof == 8 else 64
print("Guessed Tb_entry size:", self.tb_entry_size)
self.printlog_set_current_section(2)
self.printlog("\n")
self.printlog("typedef struct __attribute__((packed))\n")
self.printlog("{\n")
self.base_block_size = self.print_members(gdb.lookup_type("Tb_entry"), False)
self.printlog(" union __attribute__((__packed__))\n")
self.printlog(" {\n")
for i in sorted(tbentry_types, key=lambda t: t.name):
if i.name in self.ktrace_shortnames:
self.print_single_struct(i, self.ktrace_shortnames[i.name])
else:
raise gdb.GdbError("Missing '%s' in internal knowledge " \
"base. Please add." % (i.name))
self.printlog(" } m;\n")
self.printlog("} l4_tracebuffer_entry_t;\n")
self.printlog_set_current_section(1)
self.printlog("\n")
for i in sorted(self.typedefs.keys()):
self.printlog("typedef %s %s;\n" % (self.typedefs[i], i))
self.printlog_write()
def get_tbentry_classes(self):
print("Querying Tb_entry types. This might take a while.")
# Is there any faster way of doing this?
output = gdb.execute("info types", False, True)
regexp = re.compile('^(\S+);$') # should fetch all we need
types = []
for line in output.split('\n'):
m = regexp.match(line)
if m:
t = gdb.lookup_type(m.group(1))
if "Tb_entry" in t and t["Tb_entry"].is_base_class:
types.append(t)
return types
def invoke(self, arg, from_tty):
tbentry_types = self.get_tbentry_classes()
self.gen_ktrace_events(tbentry_types)
Fiasco_tbuf()