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:
parent
0941b66f0c
commit
8054c36f26
2 changed files with 211 additions and 3 deletions
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue