Merge remote-tracking branch 'remotes/elmarco/tags/slirp-pull-request' into staging
[qemu.git] / scripts / qemugdb / coroutine.py
1 #!/usr/bin/python
2
3 # GDB debugging support
4 #
5 # Copyright 2012 Red Hat, Inc. and/or its affiliates
6 #
7 # Authors:
8 # Avi Kivity <avi@redhat.com>
9 #
10 # This work is licensed under the terms of the GNU GPL, version 2
11 # or later. See the COPYING file in the top-level directory.
12
13 import gdb
14
15 VOID_PTR = gdb.lookup_type('void').pointer()
16
17 def get_fs_base():
18 '''Fetch %fs base value using arch_prctl(ARCH_GET_FS). This is
19 pthread_self().'''
20 # %rsp - 120 is scratch space according to the SystemV ABI
21 old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
22 gdb.execute('call (int)arch_prctl(0x1003, $rsp - 120)', False, True)
23 fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
24 gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True)
25 return fs_base
26
27 def pthread_self():
28 '''Fetch pthread_self() from the glibc start_thread function.'''
29 f = gdb.newest_frame()
30 while f.name() != 'start_thread':
31 f = f.older()
32 if f is None:
33 return get_fs_base()
34
35 try:
36 return f.read_var("arg")
37 except ValueError:
38 return get_fs_base()
39
40 def get_glibc_pointer_guard():
41 '''Fetch glibc pointer guard value'''
42 fs_base = pthread_self()
43 return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base)
44
45 def glibc_ptr_demangle(val, pointer_guard):
46 '''Undo effect of glibc's PTR_MANGLE()'''
47 return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard))
48
49 def get_jmpbuf_regs(jmpbuf):
50 JB_RBX = 0
51 JB_RBP = 1
52 JB_R12 = 2
53 JB_R13 = 3
54 JB_R14 = 4
55 JB_R15 = 5
56 JB_RSP = 6
57 JB_PC = 7
58
59 pointer_guard = get_glibc_pointer_guard()
60 return {'rbx': jmpbuf[JB_RBX],
61 'rbp': glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard),
62 'rsp': glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard),
63 'r12': jmpbuf[JB_R12],
64 'r13': jmpbuf[JB_R13],
65 'r14': jmpbuf[JB_R14],
66 'r15': jmpbuf[JB_R15],
67 'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) }
68
69 def bt_jmpbuf(jmpbuf):
70 '''Backtrace a jmpbuf'''
71 regs = get_jmpbuf_regs(jmpbuf)
72 old = dict()
73
74 for i in regs:
75 old[i] = gdb.parse_and_eval('(uint64_t)$%s' % i)
76
77 for i in regs:
78 gdb.execute('set $%s = %s' % (i, regs[i]))
79
80 gdb.execute('bt')
81
82 for i in regs:
83 gdb.execute('set $%s = %s' % (i, old[i]))
84
85 def coroutine_to_jmpbuf(co):
86 coroutine_pointer = co.cast(gdb.lookup_type('CoroutineUContext').pointer())
87 return coroutine_pointer['env']['__jmpbuf']
88
89
90 class CoroutineCommand(gdb.Command):
91 '''Display coroutine backtrace'''
92 def __init__(self):
93 gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA,
94 gdb.COMPLETE_NONE)
95
96 def invoke(self, arg, from_tty):
97 argv = gdb.string_to_argv(arg)
98 if len(argv) != 1:
99 gdb.write('usage: qemu coroutine <coroutine-pointer>\n')
100 return
101
102 bt_jmpbuf(coroutine_to_jmpbuf(gdb.parse_and_eval(argv[0])))
103
104 class CoroutineSPFunction(gdb.Function):
105 def __init__(self):
106 gdb.Function.__init__(self, 'qemu_coroutine_sp')
107
108 def invoke(self, addr):
109 return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rsp'].cast(VOID_PTR)
110
111 class CoroutinePCFunction(gdb.Function):
112 def __init__(self):
113 gdb.Function.__init__(self, 'qemu_coroutine_pc')
114
115 def invoke(self, addr):
116 return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rip'].cast(VOID_PTR)