1
0
Fork 0
mirror of https://github.com/hermitcore/libhermit.git synced 2025-03-09 00:00:03 +01:00

gdb-scripts: backtrace and context switching for tasks

These new commands allow to show backtraces of any HermitCore task as
well as completely switching the current context. This way, you can
inspect each task as if it were running, which hasn't been possible
until now, because neither GDB nor Qemu have knowledge about the task
structure of HermitCore.
This commit is contained in:
daniel-k 2016-08-26 20:49:35 +02:00
parent 0941b66f0c
commit 8054c36f26
2 changed files with 211 additions and 3 deletions

View file

@ -57,3 +57,53 @@ Note that these are not signal handlers registered by newlib
4 | 0x989660 <signal_dispatcher>
5 | 0x989660 <signal_dispatcher>
```
Show backtrace of suspended (or any) task:
```
(gdb) hermit-ps
ID | STATE | CPU | PRIO | STACK | INSTRUCTION POINTER
--------------------------------------------------------------------
0 | IDL | 0 | 0 | 0x88d000 | 0x80a756 <reschedule+41>
1 | IDL | 1 | 0 | 0x88f000 | 0x80a756 <reschedule+41>
2 | IDL | 2 | 0 | 0x891000 | 0x80a756 <reschedule+41>
3 | IDL | 3 | 0 | 0x893000 | 0x80a756 <reschedule+41>
4 | BLK | 0 | 8 | 0xde000 | 0x80a756 <reschedule+41>
5 | BLK | 0 | 16 | 0xf8000 | 0x80a756 <reschedule+41>
6 | RUN | 1 | 8 | 0x115000 | 0x990070 <thread_func+64>
7 | RUN | 2 | 8 | 0x12b000 | 0x990070 <thread_func+64>
8 | RUN | 3 | 8 | 0x141000 | 0x990070 <thread_func+64>
9 | RUN | 0 | 8 | 0x157000 | 0x990070 <thread_func+64>
(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
```

View file

@ -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<register>[\w]+)\s+(?P<value>0x[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()