qemu/scripts/qemugdb/coroutine.py
<<
>>
Prefs
   1#
   2# GDB debugging support
   3#
   4# Copyright 2012 Red Hat, Inc. and/or its affiliates
   5#
   6# Authors:
   7#  Avi Kivity <avi@redhat.com>
   8#
   9# This work is licensed under the terms of the GNU GPL, version 2
  10# or later.  See the COPYING file in the top-level directory.
  11
  12import gdb
  13
  14VOID_PTR = gdb.lookup_type('void').pointer()
  15
  16def get_fs_base():
  17    '''Fetch %fs base value using arch_prctl(ARCH_GET_FS).  This is
  18       pthread_self().'''
  19    # %rsp - 120 is scratch space according to the SystemV ABI
  20    old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
  21    gdb.execute('call (int)arch_prctl(0x1003, $rsp - 120)', False, True)
  22    fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
  23    gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True)
  24    return fs_base
  25
  26def pthread_self():
  27    '''Fetch pthread_self() from the glibc start_thread function.'''
  28    f = gdb.newest_frame()
  29    while f.name() != 'start_thread':
  30        f = f.older()
  31        if f is None:
  32            return get_fs_base()
  33
  34    try:
  35        return f.read_var("arg")
  36    except ValueError:
  37        return get_fs_base()
  38
  39def get_glibc_pointer_guard():
  40    '''Fetch glibc pointer guard value'''
  41    fs_base = pthread_self()
  42    return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base)
  43
  44def glibc_ptr_demangle(val, pointer_guard):
  45    '''Undo effect of glibc's PTR_MANGLE()'''
  46    return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard))
  47
  48def get_jmpbuf_regs(jmpbuf):
  49    JB_RBX  = 0
  50    JB_RBP  = 1
  51    JB_R12  = 2
  52    JB_R13  = 3
  53    JB_R14  = 4
  54    JB_R15  = 5
  55    JB_RSP  = 6
  56    JB_PC   = 7
  57
  58    pointer_guard = get_glibc_pointer_guard()
  59    return {'rbx': jmpbuf[JB_RBX],
  60        'rbp': glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard),
  61        'rsp': glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard),
  62        'r12': jmpbuf[JB_R12],
  63        'r13': jmpbuf[JB_R13],
  64        'r14': jmpbuf[JB_R14],
  65        'r15': jmpbuf[JB_R15],
  66        'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) }
  67
  68def bt_jmpbuf(jmpbuf):
  69    '''Backtrace a jmpbuf'''
  70    regs = get_jmpbuf_regs(jmpbuf)
  71    old = dict()
  72
  73    # remember current stack frame and select the topmost
  74    # so that register modifications don't wreck it
  75    selected_frame = gdb.selected_frame()
  76    gdb.newest_frame().select()
  77
  78    for i in regs:
  79        old[i] = gdb.parse_and_eval('(uint64_t)$%s' % i)
  80
  81    for i in regs:
  82        gdb.execute('set $%s = %s' % (i, regs[i]))
  83
  84    gdb.execute('bt')
  85
  86    for i in regs:
  87        gdb.execute('set $%s = %s' % (i, old[i]))
  88
  89    selected_frame.select()
  90
  91def co_cast(co):
  92    return co.cast(gdb.lookup_type('CoroutineUContext').pointer())
  93
  94def coroutine_to_jmpbuf(co):
  95    coroutine_pointer = co_cast(co)
  96    return coroutine_pointer['env']['__jmpbuf']
  97
  98
  99class CoroutineCommand(gdb.Command):
 100    '''Display coroutine backtrace'''
 101    def __init__(self):
 102        gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA,
 103                             gdb.COMPLETE_NONE)
 104
 105    def invoke(self, arg, from_tty):
 106        argv = gdb.string_to_argv(arg)
 107        if len(argv) != 1:
 108            gdb.write('usage: qemu coroutine <coroutine-pointer>\n')
 109            return
 110
 111        bt_jmpbuf(coroutine_to_jmpbuf(gdb.parse_and_eval(argv[0])))
 112
 113class CoroutineBt(gdb.Command):
 114    '''Display backtrace including coroutine switches'''
 115    def __init__(self):
 116        gdb.Command.__init__(self, 'qemu bt', gdb.COMMAND_STACK,
 117                             gdb.COMPLETE_NONE)
 118
 119    def invoke(self, arg, from_tty):
 120
 121        gdb.execute("bt")
 122
 123        if gdb.parse_and_eval("qemu_in_coroutine()") == False:
 124            return
 125
 126        co_ptr = gdb.parse_and_eval("qemu_coroutine_self()")
 127
 128        while True:
 129            co = co_cast(co_ptr)
 130            co_ptr = co["base"]["caller"]
 131            if co_ptr == 0:
 132                break
 133            gdb.write("Coroutine at " + str(co_ptr) + ":\n")
 134            bt_jmpbuf(coroutine_to_jmpbuf(co_ptr))
 135
 136class CoroutineSPFunction(gdb.Function):
 137    def __init__(self):
 138        gdb.Function.__init__(self, 'qemu_coroutine_sp')
 139
 140    def invoke(self, addr):
 141        return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rsp'].cast(VOID_PTR)
 142
 143class CoroutinePCFunction(gdb.Function):
 144    def __init__(self):
 145        gdb.Function.__init__(self, 'qemu_coroutine_pc')
 146
 147    def invoke(self, addr):
 148        return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rip'].cast(VOID_PTR)
 149