| #!/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) |