diff options
Diffstat (limited to 'python')
-rw-r--r-- | python/weenix/__init__.py | 204 | ||||
-rw-r--r-- | python/weenix/debug_userland.py | 25 | ||||
-rw-r--r-- | python/weenix/info.py | 20 | ||||
-rw-r--r-- | python/weenix/kmem.py | 109 | ||||
-rw-r--r-- | python/weenix/list.py | 100 | ||||
-rw-r--r-- | python/weenix/proc.py | 90 | ||||
-rw-r--r-- | python/weenix/stack.py | 56 |
7 files changed, 604 insertions, 0 deletions
diff --git a/python/weenix/__init__.py b/python/weenix/__init__.py new file mode 100644 index 0000000..58b06bf --- /dev/null +++ b/python/weenix/__init__.py @@ -0,0 +1,204 @@ +import gdb + +import weenix.stack + +_weenix_command_prefix = "kernel" +_weenix_command_names = list() + +class WeenixPrefixCommand(gdb.Command): + + def __init__(self): + gdb.Command.__init__(self, _weenix_command_prefix, gdb.COMMAND_DATA, gdb.COMPLETE_COMMAND, True) + + def invoke(self, arg, tty): + if (len(arg) != 0): + print ("'{0}' is not a valid {1} command".format(arg, _weenix_command_prefix)) + print("valid {0} commands are:".format(_weenix_command_prefix)) + for command in _weenix_command_names: + print(" {0}".format(command)) + print ("run 'help {0} <command>' for details on a particular command".format(_weenix_command_prefix)) + +WeenixPrefixCommand() + +class Command(gdb.Command): + + def __init__(self, name, command_class, completer_class=None, prefix=False): + if (len(name.split()) == 1): + _weenix_command_names.append(name) + _weenix_command_names.sort() + name = "{0} {1}".format(_weenix_command_prefix, name) + if (completer_class == None): + gdb.Command.__init__(self, name, command_class, prefix=prefix) + else: + gdb.Command.__init__(self, name, command_class, completer_class, prefix=prefix) + +_weenix_param_names = list() + +class WeenixSetPrefixCommand(gdb.Command): + + def __init__(self): + gdb.Command.__init__(self, "set " + _weenix_command_prefix, gdb.COMMAND_DATA, gdb.COMPLETE_COMMAND, True) + + def invoke(self, arg, tty): + if (len(arg) != 0): + print("'{0}' is not a valid {1} parameter".format(arg, _weenix_command_prefix)) + print("valid {0} parameters are:".format(_weenix_command_prefix)) + for param in _weenix_param_names: + print(" {0}".format(param)) + print("run 'help {0} <param>' for details on a particular parameter".format(_weenix_command_prefix)) + +WeenixSetPrefixCommand() + +class WeenixShowPrefixCommand(gdb.Command): + + def __init__(self): + gdb.Command.__init__(self, "show " + _weenix_command_prefix, gdb.COMMAND_DATA, gdb.COMPLETE_COMMAND, True) + + def invoke(self, arg, tty): + if (len(arg) != 0): + print("'{0}' is not a valid {1} parameter".format(arg, _weenix_command_prefix)) + print("valid {0} parameters are:".format(_weenix_command_prefix)) + for param in _weenix_param_names: + print(" {0}".format(param)) + print("run 'help {0} <param>' for details on a particular parameter".format(_weenix_command_prefix)) + +WeenixShowPrefixCommand() + +class Parameter(gdb.Parameter): + + def __init__(self, name, command_class, parameter_class, enum=None): + _weenix_param_names.append(name) + _weenix_param_names.sort() + name = "{0} {1}".format(_weenix_command_prefix, name) + if (None == enum): + gdb.Parameter.__init__(self, name, command_class, parameter_class) + else: + gdb.Parameter.__init__(self, name, command_class, parameter_class, enum) + +class Flag(weenix.Parameter): + + def __init__(self, name, command_class, default=False): + self._name = name + self.value = default + self._final = None + weenix.Parameter.__init__(self, name, command_class, gdb.PARAM_BOOLEAN) + weenix.Hook("boot", self.boot_callback) + + def boot_callback(self, args): + self._final = self.value + self.callback(self._final) + + def callback(self, value): + None + + def get_set_string(self): + if (None == self._final): + return "{0} is {1}".format(self._name, "enabled" if self.value else "disabled") + else: + self.value = self._final + return "{0} parameter cannot be changed once Weenix has booted".format(self._name) + + def get_show_string(self, value): + return "{0} is {1}".format(self._name, value) + +class WeenixError(gdb.GdbError): + + def __init__(self, msg): + self.__msg = msg + + def __str__(self): + return self.__msg + +class Hook(gdb.Breakpoint): + + def __init__(self, name, callback): + gdb.Breakpoint.__init__(self, "__py_hook_" + name, internal=True) + self.callback = callback + + def stop(self): + frame = weenix.stack.Frame(gdb.newest_frame()) + self.callback(frame.args()) + return False + +class TypeError(WeenixError): + + def __init__(self, actual, expected, name=None): + self.actual = actual + self.expected = expected + + clarification = "" + if (str(expected) != str(expected.strip_typedefs())): + clarification = " ('{0}')".format(expected.strip_typedefs()) + + if (name == None): + name = "value" + else: + name = "'{0}'".format(name) + + WeenixError.__init__( + self, "{0} has type '{1}', expected '{2}'{3}" + .format(name, actual, expected, clarification)) + +def assert_type(value, expected, unqualified=True): + if (type(value) == str): + try: + name = value + actual = gdb.parse_and_eval(value).type + except RuntimeError as err: + raise WeenixError(str(err)) + elif (type(value) == gdb.Value): + name = None + actual = value.type + elif (type(value) == gdb.Type): + name = None + actual = value + else: + raise RuntimeError("bad first argument: {0}".format(value)) + + expect = expected + if (unqualified): + actual = actual.unqualified() + expect = expect.unqualified() + expected = expected.unqualified() + actual = actual.strip_typedefs() + expect = expect.strip_typedefs() + if (not str(actual) == str(expect)): + raise TypeError(actual, expected, name) + +class EvalFunctionHelper(gdb.Function): + + def __init__(self): + gdb.Function.__init__(self, self.name()) + self._count = 0 + self._vals = dict() + + def name(self): + return "__wnx_val" + + def register(self, value): + if (not isinstance(value, gdb.Value)): + raise TypeError("expected gdb.Value") + + self._count += 1 + self._vals[self._count] = value + return self._count + + def invoke(self, index): + val = self._vals[int(index)] + del self._vals[int(index)] + return val + +_weenix_eval_func_helper = EvalFunctionHelper() + +def value_to_string(value): + index = _weenix_eval_func_helper.register(value) + return "${0}({1})".format(_weenix_eval_func_helper.name(), index) + +def eval_func(name, *args): + argstr = "" + for i, arg in enumerate(args): + if (isinstance(arg, gdb.Value)): + arg = value_to_string(arg) + argstr += "{0},".format(arg) + argstr = argstr[:-1] + return gdb.parse_and_eval("{0}({1})".format(name, argstr)) diff --git a/python/weenix/debug_userland.py b/python/weenix/debug_userland.py new file mode 100644 index 0000000..07e8cc3 --- /dev/null +++ b/python/weenix/debug_userland.py @@ -0,0 +1,25 @@ +from elftools.elf.elffile import ELFFile +from os import path + +class DebugUserland(gdb.Command): + def __init__(self): + super(DebugUserland, self).__init__("debug-userland", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + directory = 'user/usr/bin/' + filename = directory + arg + '.exec' + if not path.exists(filename): + filename = 'user/bin/' + arg + '.exec' + if arg == 'init': + filename = 'user/sbin/init.exec' + elf = ELFFile(open(filename, 'rb')) + text_section = elf.get_section_by_name('.text') + entry = text_section.header.sh_addr + gdb.execute(f"add-symbol-file {filename} {entry}") + + symtab = elf.get_section_by_name('.symtab') + main = symtab.get_symbol_by_name('main')[0] + main_addr = main.entry.st_value + gdb.execute(f"break *{main_addr}") + +DebugUserland() diff --git a/python/weenix/info.py b/python/weenix/info.py new file mode 100644 index 0000000..e624746 --- /dev/null +++ b/python/weenix/info.py @@ -0,0 +1,20 @@ +import gdb +import weenix + +_dbg_infofunc_type = gdb.lookup_type("dbg_infofunc_t") +_char_p_type = gdb.lookup_type("char").pointer() + +def string(infofunc, data=None): + weenix.assert_type("&" + infofunc, _dbg_infofunc_type) + + if (data == None): + data = "0" + + npages = 8 + buf = weenix.eval_func("page_alloc_n", npages) + + weenix.eval_func(infofunc, data, buf, "PAGE_SIZE") + res = buf.cast(_char_p_type).string() + + weenix.eval_func("page_free_n", buf, npages); + return res diff --git a/python/weenix/kmem.py b/python/weenix/kmem.py new file mode 100644 index 0000000..8f8a6f2 --- /dev/null +++ b/python/weenix/kmem.py @@ -0,0 +1,109 @@ +import gdb +import weenix + +import weenix.list + +PAGE_SIZE = 4096 + +_uint32_type = gdb.lookup_type("uint32_t") +_uintptr_type = gdb.lookup_type("uintptr_t") +_slab_type = gdb.lookup_type("struct slab") +_allocator_type = gdb.lookup_type("struct slab_allocator") +_bufctl_type = gdb.lookup_type("struct slab_bufctl") +_void_type = gdb.lookup_type("void") + +# for Python 3 compatibility, where xrange is done away with +try: + xrange +except NameError: + xrange = range + +class Slab: + + def __init__(self, alloc, val): + self._alloc = alloc + if (val.type.code == gdb.TYPE_CODE_PTR): + self._value = val.cast(_slab_type.pointer()).dereference() + else: + self._value = val.cast(_slab_type) + + def objs(self, typ=None): + next = self._value["s_addr"] + for i in xrange(int(self._alloc["sa_slab_nobjs"])): + bufctl = (next.cast(_uintptr_type) + + self._alloc["sa_objsize"]).cast(_bufctl_type.pointer()) + if (bufctl.dereference()["u"]["sb_slab"] == self._value.address): + # if redzones are in effect we need to skip them + if (int(next.cast(_uint32_type.pointer()).dereference()) == 0xdeadbeef): + value = (next.cast(_uint32_type.pointer()) + 1).cast(_void_type.pointer()) + else: + value = next + + if (typ != None): + yield value.cast(typ.pointer()) + else: + yield value + + next = (next.cast(_uintptr_type) + + self._alloc["sa_objsize"] + + _bufctl_type.sizeof).cast(_void_type.pointer()) + +class SlabAllocator: + + def __init__(self, val): + if (val.type.code == gdb.TYPE_CODE_PTR): + self._value = val.cast(_allocator_type.pointer()).dereference() + else: + self._value = val.cast(_allocator_type) + + def name(self): + return self._value["sa_name"].string() + + def size(self): + return int(self._value["sa_objsize"]) + + def slabs(self): + next = self._value["sa_slabs"] + while (next != 0): + yield Slab(self._value, next.dereference()) + next = next.dereference()["s_next"] + + def objs(self, typ=None): + for slab in self.slabs(): + for obj in slab.objs(typ): + yield obj + + def __str__(self): + res = "name: {0}\n".format(self.name()) + res += "slabcount: {0}\n".format(len(list(self.slabs()))) + res += "objsize: {0}\n".format(self.size()) + res += "objcount: {0}".format(len(list(self.objs()))) + return res + +def allocators(): + next = gdb.parse_and_eval("slab_allocators") + while (next != 0): + yield SlabAllocator(next.dereference()) + next = next.dereference()["sa_next"] + +def allocator(name): + for alloc in allocators(): + if name == alloc.name(): + return alloc + raise KeyError(name) + +def pagesize(): + return PAGE_SIZE + +def freepages(): + freepages = dict() + for pagegroup in weenix.list.load("pagegroup_list", "struct pagegroup", "pg_link"): + freelist = pagegroup.item()["pg_freelist"] + for order in xrange(freelist.type.sizeof // freelist.type.target().sizeof): + psize = (1 << order) * PAGE_SIZE + count = len(weenix.list.load(freelist[order])) + if (order in freepages): + freepages[order] += count + else: + freepages[order] = count + return freepages diff --git a/python/weenix/list.py b/python/weenix/list.py new file mode 100644 index 0000000..6f951ae --- /dev/null +++ b/python/weenix/list.py @@ -0,0 +1,100 @@ +import gdb +import weenix + +_char_type = gdb.lookup_type("char") +_list_type = gdb.lookup_type("list_t") +_list_link_type = gdb.lookup_type("list_link_t") + +class Link: + + def __init__(self, value, dtype=None, dmemb=None): + self._value = value + self._dtype = dtype + self._dmemb = dmemb + + def value(self): + return self._value + + def item(self, typ=None, memb=None): + if (typ == None): + typ = self._dtype + if (memb == None): + memb = self._dmemb + if (typ == None or memb == None): + raise RuntimeError("list reference requires " + "both type and member name") + + for field in gdb.lookup_type(typ).fields(): + if (field.name == memb): + link = self._value.address.cast(_char_type.pointer()) + link -= (field.bitpos // 8) + link = link.cast(gdb.lookup_type(typ).pointer()) + return link.dereference() + raise weenix.WeenixError("no member {0} of {1}" + .format(memb, typ)) + + def link_addr(self): + return self._value.address + +class List: + + def __init__(self, value, dtype=None, dmemb=None): + self._value = value + self._dtype = dtype + self._dmemb = dmemb + + def __iter__(self): + curr = self._value["l_next"].dereference() + while (curr.address != self._value.address): + yield Link(curr, self._dtype, self._dmemb) + curr = curr["l_next"].dereference() + raise StopIteration + + def __len__(self): + try: + return self.__count + except AttributeError: + self.__count = 0 + curr = self._value["l_next"].dereference() + while (curr.address != self._value.address): + curr = curr["l_next"].dereference() + self.__count += 1 + return self.__count + + def __getitem__(self, key): + if (type(key) != int): + raise TypeError(key) + + for i, item in enumerate(self): + if (i == key): + return item + raise IndexError(key) + +def load(name, dtype=None, dmemb=None): + weenix.assert_type(name, _list_type) + + if (dtype != None): + try: + if (not isinstance(dtype, gdb.Type)): + typ = gdb.lookup_type(dtype) + else: + typ = dtype + except RuntimeError: + raise gdb.GdbError("no such type: {0}".format(dtype)) + + found = False + for field in typ.strip_typedefs().fields(): + if (field.name == dmemb): + try: + weenix.assert_type(field.type, _list_link_type) + except gdb.GdbError as err: + raise gdb.GdbError( + "field '{0}' of type '{1}' has wrong type: {2}" + .format(dmemb, dtype, str(err))) + found = True + if (not found): + raise gdb.GdbError("'{0}' type does not contain field '{1}'" + .format(dtype, dmemb)) + + value = name if isinstance(name, gdb.Value) else gdb.parse_and_eval(name) + return List(value, dtype, dmemb) diff --git a/python/weenix/proc.py b/python/weenix/proc.py new file mode 100644 index 0000000..14ffa1d --- /dev/null +++ b/python/weenix/proc.py @@ -0,0 +1,90 @@ +import gdb +import weenix +import weenix.list + +_proc_states = { + 0 : "RUNNING", + 1 : "EXITED" +} + +class Proc: + + def __init__(self, val): + self._val = val + + def name(self): + return self._val["p_name"].string() + + def pid(self): + return int(self._val["p_pid"]) + + def state(self): + return _proc_states[int(self._val["p_state"])] + + def status(self): + return int(self._val["p_status"]) + + def parent(self): + proc = self._val["p_pproc"] + if (proc == 0): + return None + else: + return Proc(proc.dereference()) + + def children(self): + for child in weenix.list.load(self._val["p_children"], "struct proc", "p_child_link"): + yield Proc(child.item()) + + def str_short(self): + res = "{0:>5} ({1}) {2}".format(self.pid(), self.name(), self.state()) + if (self.state() == "EXITED"): + res = "{0} ({1})".format(res, self.status()) + if (self == curproc()): + res = "\033[1m{0}\033[22m".format(res) + return res + + def __eq__(self, other): + if (not isinstance(other, Proc)): + return False + else: + return self.pid() == other.pid() + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + res = "PID: {0} ({1})\n".format(self.pid(), self.name()) + if (self == curproc()): + res = "\033[1m{0}\033[22m".format(res) + if (self.state() == "EXITED"): + res += "{0} ({1})\n".format(self.state(), self.status()) + else: + res += "{0}\n".format(self.state()) + if (self.parent() != None): + res += "Parent:\n" + res += "{0}\n".format(self.parent().str_short()) + if (len(list(self.children())) > 0): + res += "Children:\n" + for child in self.children(): + res += "{0}\n".format(child.str_short()) + return res + +def iter(): + for link in weenix.list.load("proc_list", "struct proc", "p_list_link"): + yield Proc(link.item()) + +def lookup(pid): + return Proc(weenix.eval_func("proc_lookup", pid).dereference()) + +def curproc(): + return Proc(gdb.parse_and_eval("curproc")) + +def str_proc_tree(proc=None, indent=""): + if (proc == None): + proc = lookup(0) + + res = "{0}| {1}\n".format(indent, proc.str_short()) + + for child in proc.children(): + res += str_proc_tree(child, indent+" ") + return res diff --git a/python/weenix/stack.py b/python/weenix/stack.py new file mode 100644 index 0000000..c7abdfd --- /dev/null +++ b/python/weenix/stack.py @@ -0,0 +1,56 @@ +import gdb +import weenix + +class Stack: + + def __init__(self, gdbframe=None): + if (gdbframe == None): + gdbframe = gdb.newest_frame() + self._frames = [] + while (None != gdbframe): + self._frames.append(weenix.stack.Frame(gdbframe)) + gdbframe = gdbframe.older() + + def contains(self, fname): + for frame in self._frames: + if (frame._func == fname): + return True + return False + + def __str__(self): + res = "" + for i, frame in enumerate(self._frames): + res += "#{0:<2} {1}\n".format(i, str(frame)) + return res + +class Frame: + + def __init__(self, gdbframe): + self._func = "???" if gdbframe.name() == None else gdbframe.name() + self._pc = gdbframe.find_sal().pc + self._symtab = gdbframe.find_sal() + self._args = {} + if (gdbframe.function() != None): + gdbframe.select() + argstr = gdb.execute("info args", to_string=True) + for line in argstr.split("\n"): + parts = line.split("=", 1) + if (len(parts) == 2): + self._args[parts[0].strip()] = parts[1].strip() + + def args(self): + return self._args + + def __str__(self, line=0): + if (self._symtab.symtab == None): + res = "0x{1:>08x} ?? ()".format(line, self._pc) + else: + hasargs = False + res = "0x{1:>08x} {2} (".format(line, self._pc, self._func) + for arg in self._args.iterkeys(): + hasargs = True + res += arg + "=" + self._args[arg] + ", " + if (hasargs): + res = res[:-2] + res += ") in {0}:{1}".format(self._symtab.symtab.filename, self._symtab.line) + return res |