blob: b4ed8acda4e31f3c997b90b7fd9704675fb0387d [file]
#!/usr/bin/env python
import os
import os.path
from glob import glob
def source(callable):
"""Convenience decorator for sourcing gdb commands"""
callable()
return callable
@source
class seL4(gdb.Command):
"""Utility functions for debugging seL4 applications"""
def __init__(self):
super(self.__class__, self).__init__('sel4', gdb.COMMAND_USER, prefix=True)
@source
class seL4BreakpointsEnabled(gdb.Command):
"""Enable or disable breakpoints (disabled by default)"""
def __init__(self):
super(self.__class__, self).__init__('sel4 breakpoints-enabled', gdb.COMMAND_STATUS)
def complete(self, text, words):
return [c for c in ['True', 'False'] if c.startswith(text)]
def invoke(self, arg, from_tty):
if arg:
breakpoints_enabled(arg)
else:
print(breakpoints_enabled())
@source
class seL4Threads(gdb.Command):
"""Lists all seL4 threads"""
def __init__(self):
super(self.__class__, self).__init__('sel4 threads', gdb.COMMAND_STATUS)
def invoke(self, arg, from_tty):
print('\n'.join(get_threads()))
@source
class seL4Thread(gdb.Command):
"""Returns current seL4 thread"""
def __init__(self):
super(self.__class__, self).__init__('sel4 thread', gdb.COMMAND_STATUS)
def invoke(self, arg, from_tty):
print(get_current_thread())
@source
class seL4WaitForThread(gdb.Command):
"""Waits for given thread to be avaiable"""
def __init__(self):
super(self.__class__, self).__init__('sel4 wait-for-thread', gdb.COMMAND_BREAKPOINTS)
def complete(self, text, word):
return list(thread for thread in get_threads() if thread.startswith(text))
def invoke(self, arg, from_tty):
gdb.execute('mon seL4 WaitForThread "{}"'.format(arg))
gdb.execute('continue')
@source
class seL4SwitchSymbols(gdb.Command):
"""Forcibly switches symbols to ones of the given component
sel4 switch-symbols [component]
Forcibly sets symbol-file to file corresponding to given component.
If no comonent name is given or thread is "default" then default
binary is loaded instead."""
def __init__(self):
super(self.__class__, self).__init__('sel4 switch-symbols', gdb.COMMAND_FILES)
def complete(self, text, word):
global BINARIES
components = list(BINARIES.keys())
components.append('default')
return list(component for component in components if component.startswith(text))
def invoke(self, arg, from_tty):
if not arg or arg == 'default':
switch_symbols(verbose=True)
else:
switch_symbols(arg, fallback=False, verbose=True)
@source
class seL4Break(gdb.Command):
"""Creates breakpoint
sel4 break thread [address]
Creates breakpoint in <thread> on optional <address>
If no address is given, the breakpoint will be set on
first address after context-switch"""
def __init__(self):
super(self.__class__, self).__init__('sel4 break', gdb.COMMAND_BREAKPOINTS)
def complete(self, text, word):
words = text.split(' ')
if not text or len(words) <= 1:
return list(thread for thread in get_threads() if thread.startswith(word))
elif len(words) == 2:
return gdb.COMPLETE_SYMBOL
else:
return gdb.COMPLETE_NONE
def invoke(self, arg, from_tty):
args = arg.split(' ')
if len(args) < 1 or not args[0]:
raise gdb.GdbError("sel4 break: thread argument is required")
thread = args[0] if len(args) >= 1 else None
symbol_name = args[1] if len(args) >= 2 else None
if symbol_name:
symbol, _ = gdb.lookup_symbol(symbol_name)
address = int(symbol.value().address)
gdb.execute('mon seL4 SetBreakpoint "{}" {}'.format(thread, hex(address)))
else:
gdb.execute('mon seL4 SetBreakpoint "{}"'.format(thread))
@source
class seL4Delete(gdb.Command):
"""Removes breakpoint
sel4 delete thread [address]
Removes given breakpoint. If no address is given, the context-switch
breakpoint is removed"""
def __init__(self):
super(self.__class__, self).__init__('sel4 delete', gdb.COMMAND_BREAKPOINTS)
def complete(self, text, word):
words = text.split(' ')
if not text or len(words) <= 1:
return list(thread for thread in get_threads() if thread.startswith(word))
elif len(words) == 2:
breakpoints = get_breakpoints(words[0])
lines = breakpoints.split('\n')[3:][:-1]
completions = []
for line in lines:
values = line.split('|')
thread = values[1].strip()
if words[0] not in thread:
continue
if 'null' in values[2]:
continue
address = values[2].strip();
if address.startswith(word):
completions.append(address)
return completions
else:
return gdb.COMPLETE_NONE
def invoke(self, arg, from_tty):
args = arg.split(' ')
if len(args) < 1 or not args[0]:
raise gdb.GdbError("sel4 delete: thread argument is required")
thread = args[0] if len(args) >= 1 else None
address = args[1] if len(args) >= 2 else None
if address:
gdb.execute('mon seL4 RemoveBreakpoint "{}" {}'.format(thread, address))
else:
gdb.execute('mon seL4 RemoveBreakpoint "{}"'.format(thread))
@source
class seL4DeleteAll(gdb.Command):
"""Removes all breakpoints assigned to thread
sel4 delete-all thread"""
def __init__(self):
super(self.__class__, self).__init__('sel4 delete-all', gdb.COMMAND_BREAKPOINTS)
def complete(self, text, word):
return list(thread for thread in get_threads() if thread.startswith(text))
def invoke(self, arg, from_tty):
gdb.execute('mon seL4 RemoveBreakpoint "{}" 0x0 True'.format(arg))
@source
class seL4ListBreakpoints(gdb.Command):
"""Lists all breakpoints
sel4 list-breakpoints [thread]
"""
def __init__(self):
super(self.__class__, self).__init__('sel4 list-breakpoints', gdb.COMMAND_BREAKPOINTS)
def complete(self, text, word):
return list(thread for thread in get_threads() if thread.startswith(text))
def invoke(self, arg, from_tty):
print(get_breakpoints())
@source
class seL4Ready(gdb.Command):
"""Returns ready state of seL4 extensions"""
def __init__(self):
super(self.__class__, self).__init__('sel4 ready', gdb.COMMAND_STATUS)
def invoke(self, arg, from_tty):
gdb.execute('mon seL4 Ready')
@source
def find_binaries():
global BINARIES, DEFAULT_BINARY
BINARIES = {}
out = os.environ['OUT']
kata_path = os.path.join(out, 'kata', 'riscv32-unknown-elf', 'debug')
DEFAULT_BINARY = os.path.join(kata_path, 'kernel', 'kernel.elf')
images_path = os.path.join(kata_path, '*.instance.bin')
for image_path in glob(images_path):
image_name = os.path.basename(image_path).split('.instance.bin')[0]
BINARIES[image_name] = image_path
BINARIES['rootserver'] = os.path.join(kata_path, 'capdl-loader')
def get_breakpoints(thread=None):
if not thread:
breakpoints = gdb.execute('mon seL4 GetBreakpoints', to_string=True).strip()
else:
breakpoints = gdb.execute('mon seL4 GetBreakpoints \"{}\"'.format(thread), to_string=True).strip()
return breakpoints
def get_current_thread():
return gdb.execute('mon seL4 CurrentThread', to_string=True).strip()
def get_threads():
threads = ', '.join(gdb.execute('mon seL4 Threads', to_string=True).strip().split('\n')[1:-1])
return [thread.strip() for thread in threads.split(',') if thread.strip()]
def breakpoints_enabled(value=None):
if value:
gdb.execute('mon seL4 BreakpointsEnabled {}'.format(value))
else:
return gdb.execute('mon seL4 BreakpointsEnabled', to_string=True).strip()
def switch_symbols(thread=None, fallback=True, verbose=False):
global BINARIES, DEFAULT_BINARY
if thread is None:
gdb.execute('symbol-file {}'.format(DEFAULT_BINARY), from_tty=verbose)
return
image_name = next((key for key in BINARIES.keys() if key in thread), None)
if image_name is not None:
image_path = BINARIES[image_name]
elif image_name is None and fallback:
image_path = DEFAULT_BINARY
else:
raise gdb.GdbError("no symbol-file found for thread/component {}".format(thread))
gdb.execute('symbol-file {}'.format(image_path), from_tty=verbose)
def stop_handler(event):
if not isinstance(event, gdb.StopEvent):
return
thread = get_current_thread()
switch_symbols(thread)
gdb.events.stop.connect(stop_handler)