aboutsummaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/weenix/__init__.py204
-rw-r--r--python/weenix/debug_userland.py25
-rw-r--r--python/weenix/info.py20
-rw-r--r--python/weenix/kmem.py109
-rw-r--r--python/weenix/list.py100
-rw-r--r--python/weenix/proc.py90
-rw-r--r--python/weenix/stack.py56
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