diff --git a/hermit/usr/gdb/README.md b/hermit/usr/gdb/README.md index 7f458fa60..8547d2cb8 100644 --- a/hermit/usr/gdb/README.md +++ b/hermit/usr/gdb/README.md @@ -57,3 +57,53 @@ Note that these are not signal handlers registered by newlib 4 | 0x989660 5 | 0x989660 ``` + +Show backtrace of suspended (or any) task: + +``` +(gdb) hermit-ps + ID | STATE | CPU | PRIO | STACK | INSTRUCTION POINTER +-------------------------------------------------------------------- + 0 | IDL | 0 | 0 | 0x88d000 | 0x80a756 + 1 | IDL | 1 | 0 | 0x88f000 | 0x80a756 + 2 | IDL | 2 | 0 | 0x891000 | 0x80a756 + 3 | IDL | 3 | 0 | 0x893000 | 0x80a756 + 4 | BLK | 0 | 8 | 0xde000 | 0x80a756 + 5 | BLK | 0 | 16 | 0xf8000 | 0x80a756 + 6 | RUN | 1 | 8 | 0x115000 | 0x990070 + 7 | RUN | 2 | 8 | 0x12b000 | 0x990070 + 8 | RUN | 3 | 8 | 0x141000 | 0x990070 + 9 | RUN | 0 | 8 | 0x157000 | 0x990070 +(gdb) hermit-bt 4 +#0 0x000000000081701c in rollback () +#1 0x000000000080a756 in reschedule () at kernel/tasks.c:909 +#2 0x0000000000813f7b in timer_wait (ticks=50) at arch/x86/kernel/timer.c:139 +#3 0x000000000080b9f7 in sys_msleep (ms=500) at kernel/syscall.c:357 +#4 0x000000000098fd5e in main () +``` + +Switch current context to any task to investigate call stack. Remember to +restore the context if you want to continue execution. + +``` +(gdb) info threads + Id Target Id Frame +* 1 Thread 1 (CPU#0 [running]) 0x0000000000990070 in thread_func () + 2 Thread 2 (CPU#1 [running]) 0x0000000000990070 in thread_func () + 3 Thread 3 (CPU#2 [running]) 0x0000000000990070 in thread_func () + 4 Thread 4 (CPU#3 [running]) 0x0000000000990070 in thread_func () +(gdb) hermit-switch-context 4 +(gdb) info threads + Id Target Id Frame +* 1 Thread 1 (CPU#0 [running]) 0x000000000081701c in rollback () + 2 Thread 2 (CPU#1 [running]) 0x0000000000990070 in thread_func () + 3 Thread 3 (CPU#2 [running]) 0x0000000000990070 in thread_func () + 4 Thread 4 (CPU#3 [running]) 0x0000000000990070 in thread_func () +(gdb) bt +#0 0x000000000081701c in rollback () +#1 0x000000000080a756 in reschedule () at kernel/tasks.c:909 +#2 0x0000000000813f7b in timer_wait (ticks=50) at arch/x86/kernel/timer.c:139 +#3 0x000000000080b9f7 in sys_msleep (ms=500) at kernel/syscall.c:357 +#4 0x000000000098fd5e in main () +(gdb) hermit-restore-context +``` diff --git a/hermit/usr/gdb/hermit/tasks.py b/hermit/usr/gdb/hermit/tasks.py index bf2a36d15..879139059 100644 --- a/hermit/usr/gdb/hermit/tasks.py +++ b/hermit/usr/gdb/hermit/tasks.py @@ -136,9 +136,6 @@ class HermitLsSighandler(gdb.Command): gdb.write(header) gdb.write((len(header) - 1) * '-' + '\n') - inferior = gdb.selected_inferior() - currentInferiorThread = gdb.selected_thread() - for task in task_lists(): gdb.write(rowfmt.format( @@ -147,3 +144,164 @@ class HermitLsSighandler(gdb.Command): )) HermitLsSighandler() + + + +def stripSymbol(value): + s = "%s" % value + return s.split(' ')[0] + +class HermitTaskState: + def __init__(self, address = None): + import re + self.info_reg_regex = re.compile("(?P[\w]+)\s+(?P0x[0-9a-f]+).*") + + if address: + self.address = address + + self.registers = { + 'gs': self.address + 0, + 'fs': self.address + 1, + 'r15': self.address + 2, + 'r14': self.address + 3, + 'r13': self.address + 4, + 'r12': self.address + 5, + 'r11': self.address + 6, + 'r10': self.address + 7, + 'r9': self.address + 8, + 'r8': self.address + 9, + 'rdi': self.address + 10, + 'rsi': self.address + 11, + 'rbp': self.address + 12, + 'rsp': self.address + 13, + 'rbx': self.address + 14, + 'rdx': self.address + 15, + 'rcx': self.address + 16, + 'rax': self.address + 17, + # int_no + # error + 'rip': self.address + 20, + 'cs': self.address + 21, + 'eflags': self.address + 22, + # userrsp + 'ss': self.address + 24, + } + + # make nice strings out of register values + for register, valptr in self.registers.items(): + self.registers[register] = stripSymbol(valptr.dereference()) + + else: + self.address = False + self.info_registers = gdb.execute('info registers', to_string=True) + self.registers = {} + for line in self.info_registers.split('\n'): + match = self.info_reg_regex.match(line) + if match: + self.registers[match.group('register')] = match.group('value') + + def switch(self): + for register, value in self.registers.items(): + try: + gdb.execute("set $%s = %s" % (register, value)) + except: + print("Cannot restore %s=%s, skipping ..." % (register, value)) + + +class HermitTaskBacktrace(gdb.Command): + """Show backtrace for HermitCore task. + +Usage: hermit-bt ID""" + + def __init__(self): + super(HermitTaskBacktrace, self).__init__("hermit-bt", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + argv = gdb.string_to_argv(arg) + if len(argv) != 1: + raise gdb.GdbError("hermit-bt takes one argument") + + task = get_task_by_pid(int(argv[0])) + + if task['status'] == 2: + gdb.execute('bt') + return + + current_state = HermitTaskState() + + task_state = HermitTaskState(task['last_stack_pointer']) + + try: + task_state.switch() + gdb.execute('bt') + finally: + current_state.switch() + +HermitTaskBacktrace() + +original_state = {} + +def saveCurrentState(state): + curr_thread = gdb.selected_thread() + for thread in gdb.selected_inferior().threads(): + if not thread.num in state: + thread.switch() + state[thread.num] = HermitTaskState() + curr_thread.switch() + +def restoreCurrentState(state): + curr_thread = gdb.selected_thread() + for thread in gdb.selected_inferior().threads(): + if thread.num in state: + thread.switch() + state[thread.num].switch() + curr_thread.switch() + state = {} + +class HermitSwitchContext(gdb.Command): + """Switch current context to given HermitCore task + +Usage: hermit-switch-context ID""" + + def __init__(self): + super(HermitSwitchContext, self).__init__("hermit-switch-context", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + global original_state + + argv = gdb.string_to_argv(arg) + if len(argv) != 1: + raise gdb.GdbError("hermit-switch-context takes one argument") + + # save original state to go back to it later + saveCurrentState(original_state) + + task = get_task_by_pid(int(argv[0])) + + # switch current inferior thread to where task has run last + for thread in gdb.selected_inferior().threads(): + if (thread.num - 1) == task['last_core']: + thread.switch() + break + + # apply it's state + task_state = HermitTaskState(task['last_stack_pointer']) + task_state.switch() + +HermitSwitchContext() + + +class HermitRestoreContext(gdb.Command): + """Restore context to state before it was switched + +Usage: hermit-restore-context""" + + def __init__(self): + super(HermitRestoreContext, self).__init__("hermit-restore-context", gdb.COMMAND_DATA) + + def invoke(self, arg, from_tty): + global original_state + + restoreCurrentState(original_state) + +HermitRestoreContext()