aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/analyze-migration.py13
-rw-r--r--scripts/qapi-commands.py4
-rw-r--r--scripts/qapi-types.py68
-rw-r--r--scripts/qapi-visit.py80
-rw-r--r--scripts/qapi.py25
-rw-r--r--scripts/qemu-gdb.py6
-rw-r--r--scripts/qemugdb/aio.py58
-rw-r--r--scripts/qemugdb/coroutine.py90
8 files changed, 233 insertions, 111 deletions
diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py
index f6894be..1455387 100755
--- a/scripts/analyze-migration.py
+++ b/scripts/analyze-migration.py
@@ -252,6 +252,15 @@ class HTABSection(object):
def getDict(self):
return ""
+
+class ConfigurationSection(object):
+ def __init__(self, file):
+ self.file = file
+
+ def read(self):
+ name_len = self.file.read32()
+ name = self.file.readstr(len = name_len)
+
class VMSDFieldGeneric(object):
def __init__(self, desc, file):
self.file = file
@@ -474,6 +483,7 @@ class MigrationDump(object):
QEMU_VM_SECTION_FULL = 0x04
QEMU_VM_SUBSECTION = 0x05
QEMU_VM_VMDESCRIPTION = 0x06
+ QEMU_VM_CONFIGURATION = 0x07
QEMU_VM_SECTION_FOOTER= 0x7e
def __init__(self, filename):
@@ -514,6 +524,9 @@ class MigrationDump(object):
section_type = file.read8()
if section_type == self.QEMU_VM_EOF:
break
+ elif section_type == self.QEMU_VM_CONFIGURATION:
+ section = ConfigurationSection(file)
+ section.read()
elif section_type == self.QEMU_VM_SECTION_START or section_type == self.QEMU_VM_SECTION_FULL:
section_id = file.read32()
name = file.readstr()
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 43a893b..561e47a 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -175,7 +175,9 @@ def gen_marshal(name, arg_type, ret_type):
ret += gen_marshal_input_visit(arg_type)
ret += gen_call(name, arg_type, ret_type)
- if re.search('^ *goto out;', ret, re.MULTILINE):
+ # 'goto out' produced by gen_marshal_input_visit->gen_visit_fields()
+ # for each arg_type member, and by gen_call() for ret_type
+ if (arg_type and arg_type.members) or ret_type:
ret += mcgen('''
out:
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index 4fe618e..b37900f 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -36,26 +36,37 @@ struct %(c_name)s {
c_name=c_name(name), c_type=element_type.c_type())
-def gen_struct_field(name, typ, optional):
+def gen_struct_field(member):
ret = ''
- if optional:
+ if member.optional:
ret += mcgen('''
bool has_%(c_name)s;
''',
- c_name=c_name(name))
+ c_name=c_name(member.name))
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
- c_type=typ.c_type(), c_name=c_name(name))
+ c_type=member.type.c_type(), c_name=c_name(member.name))
return ret
-def gen_struct_fields(members):
+def gen_struct_fields(local_members, base=None):
ret = ''
- for memb in members:
- ret += gen_struct_field(memb.name, memb.type, memb.optional)
+ if base:
+ ret += mcgen('''
+ /* Members inherited from %(c_name)s: */
+''',
+ c_name=base.c_name())
+ for memb in base.members:
+ ret += gen_struct_field(memb)
+ ret += mcgen('''
+ /* Own members: */
+''')
+
+ for memb in local_members:
+ ret += gen_struct_field(memb)
return ret
@@ -66,16 +77,13 @@ struct %(c_name)s {
''',
c_name=c_name(name))
- if base:
- ret += gen_struct_field('base', base, False)
-
- ret += gen_struct_fields(members)
+ ret += gen_struct_fields(members, base)
# Make sure that all structs have at least one field; this avoids
# potential issues with attempting to malloc space for zero-length
# structs in C, and also incompatibility with C++ (where an empty
# struct is size 1).
- if not base and not members:
+ if not (base and base.members) and not members:
ret += mcgen('''
char qapi_dummy_field_for_empty_struct;
''')
@@ -87,6 +95,19 @@ struct %(c_name)s {
return ret
+def gen_upcast(name, base):
+ # C makes const-correctness ugly. We have to cast away const to let
+ # this function work for both const and non-const obj.
+ return mcgen('''
+
+static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
+{
+ return (%(base)s *)obj;
+}
+''',
+ c_name=c_name(name), base=base.c_name())
+
+
def gen_alternate_qtypes_decl(name):
return mcgen('''
@@ -126,19 +147,9 @@ struct %(c_name)s {
''',
c_name=c_name(name))
if base:
- ret += mcgen('''
- /* Members inherited from %(c_name)s: */
-''',
- c_name=c_name(base.name))
- ret += gen_struct_fields(base.members)
- ret += mcgen('''
- /* Own members: */
-''')
+ ret += gen_struct_fields([], base)
else:
- ret += mcgen('''
- %(c_type)s kind;
-''',
- c_type=c_name(variants.tag_member.type.name))
+ ret += gen_struct_field(variants.tag_member)
# FIXME: What purpose does data serve, besides preventing a union that
# has a branch named 'data'? We use it in qapi-visit.py to decide
@@ -152,10 +163,7 @@ struct %(c_name)s {
union { /* union tag is @%(c_name)s */
void *data;
''',
- # TODO ugly special case for simple union
- # Use same tag name in C as on the wire to get rid of
- # it, then: c_name=c_name(variants.tag_member.name)
- c_name=c_name(variants.tag_name or 'kind'))
+ c_name=c_name(variants.tag_member.name))
for var in variants.variants:
# Ugly special case for simple union TODO get rid of it
@@ -167,7 +175,7 @@ struct %(c_name)s {
c_name=c_name(var.name))
ret += mcgen('''
- };
+ } u;
};
''')
@@ -265,6 +273,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
self.decl += gen_union(name, base, variants)
else:
self.decl += gen_struct(name, base, members)
+ if base:
+ self.decl += gen_upcast(name, base)
self._gen_type_cleanup(name)
def visit_alternate_type(self, name, info, variants):
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index d0759d7..f40c3c7 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -15,7 +15,12 @@
from qapi import *
import re
+# visit_type_FOO_implicit() is emitted as needed; track if it has already
+# been output.
implicit_structs_seen = set()
+
+# visit_type_FOO_fields() is always emitted; track if a forward declaration
+# or implementation has already been output.
struct_fields_seen = set()
@@ -29,19 +34,24 @@ void visit_type_%(c_name)s(Visitor *v, %(c_type)sobj, const char *name, Error **
c_name=c_name(name), c_type=c_type)
-def gen_visit_implicit_struct(typ):
- if typ in implicit_structs_seen:
- return ''
- implicit_structs_seen.add(typ)
-
+def gen_visit_fields_decl(typ):
ret = ''
if typ.name not in struct_fields_seen:
- # Need a forward declaration
ret += mcgen('''
static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
''',
c_type=typ.c_name())
+ struct_fields_seen.add(typ.name)
+ return ret
+
+
+def gen_visit_implicit_struct(typ):
+ if typ in implicit_structs_seen:
+ return ''
+ implicit_structs_seen.add(typ)
+
+ ret = gen_visit_fields_decl(typ)
ret += mcgen('''
@@ -62,13 +72,12 @@ static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error *
def gen_visit_struct_fields(name, base, members):
- struct_fields_seen.add(name)
-
ret = ''
if base:
- ret += gen_visit_implicit_struct(base)
+ ret += gen_visit_fields_decl(base)
+ struct_fields_seen.add(name)
ret += mcgen('''
static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp)
@@ -80,14 +89,15 @@ static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **e
if base:
ret += mcgen('''
- visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
+ visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
''',
- c_type=base.c_name(), c_name=c_name('base'))
+ c_type=base.c_name())
ret += gen_err_check()
ret += gen_visit_fields(members, prefix='(*obj)->')
- if re.search('^ *goto out;', ret, re.MULTILINE):
+ # 'goto out' produced for base, and by gen_visit_fields() for each member
+ if base or members:
ret += mcgen('''
out:
@@ -179,18 +189,18 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
if (err) {
goto out;
}
- visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
+ visit_get_next_type(v, (int*) &(*obj)->type, %(c_name)s_qtypes, name, &err);
if (err) {
goto out_obj;
}
- switch ((*obj)->kind) {
+ switch ((*obj)->type) {
''',
c_name=c_name(name))
for var in variants.variants:
ret += mcgen('''
case %(case)s:
- visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err);
+ visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, name, &err);
break;
''',
case=c_enum_const(variants.tag_member.type.name,
@@ -218,8 +228,7 @@ def gen_visit_union(name, base, variants):
ret = ''
if base:
- members = [m for m in base.members if m != variants.tag_member]
- ret += gen_visit_struct_fields(name, None, members)
+ ret += gen_visit_fields_decl(base)
for var in variants.variants:
# Ugly special case for simple union TODO get rid of it
@@ -244,31 +253,24 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
if base:
ret += mcgen('''
- visit_type_%(c_name)s_fields(v, obj, &err);
+ visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
''',
- c_name=c_name(name))
- ret += gen_err_check(label='out_obj')
-
- tag_key = variants.tag_member.name
- if not variants.tag_name:
- # we pointlessly use a different key for simple unions
- tag_key = 'type'
- ret += mcgen('''
+ c_name=base.c_name())
+ else:
+ ret += mcgen('''
visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
- if (err) {
- goto out_obj;
- }
- if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
+''',
+ c_type=variants.tag_member.type.c_name(),
+ c_name=c_name(variants.tag_member.name),
+ name=variants.tag_member.name)
+ ret += gen_err_check(label='out_obj')
+ ret += mcgen('''
+ if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
goto out_obj;
}
switch ((*obj)->%(c_name)s) {
''',
- c_type=variants.tag_member.type.c_name(),
- # TODO ugly special case for simple union
- # Use same tag name in C as on the wire to get rid of
- # it, then: c_name=c_name(variants.tag_member.name)
- c_name=c_name(variants.tag_name or 'kind'),
- name=tag_key)
+ c_name=c_name(variants.tag_member.name))
for var in variants.variants:
# TODO ugly special case for simple union
@@ -280,13 +282,13 @@ void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error
var.name))
if simple_union_type:
ret += mcgen('''
- visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "data", &err);
+ visit_type_%(c_type)s(v, &(*obj)->u.%(c_name)s, "data", &err);
''',
c_type=simple_union_type.c_name(),
c_name=c_name(var.name))
else:
ret += mcgen('''
- visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
+ visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
''',
c_type=var.type.c_name(),
c_name=c_name(var.name))
@@ -302,7 +304,7 @@ out_obj:
error_propagate(errp, err);
err = NULL;
if (*obj) {
- visit_end_union(v, !!(*obj)->data, &err);
+ visit_end_union(v, !!(*obj)->u.data, &err);
}
error_propagate(errp, err);
err = NULL;
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 9d53255..7c50cc4 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -172,7 +172,7 @@ class QAPISchemaParser(object):
if self.tok == '#':
self.cursor = self.src.find('\n', self.cursor)
- elif self.tok in ['{', '}', ':', ',', '[', ']']:
+ elif self.tok in "{}:,[]":
return
elif self.tok == "'":
string = ''
@@ -376,7 +376,9 @@ def check_name(expr_info, source, name, allow_optional=False,
# code always prefixes it with the enum name
if enum_member:
membername = '_' + membername
- if not valid_name.match(membername):
+ # Reserve the entire 'q_' namespace for c_name()
+ if not valid_name.match(membername) or \
+ c_name(membername, False).startswith('q_'):
raise QAPIExprError(expr_info,
"%s uses invalid name '%s'" % (source, name))
@@ -390,10 +392,10 @@ def add_name(name, info, meta, implicit=False):
raise QAPIExprError(info,
"%s '%s' is already defined"
% (all_names[name], name))
- if not implicit and name[-4:] == 'Kind':
+ if not implicit and (name.endswith('Kind') or name.endswith('List')):
raise QAPIExprError(info,
- "%s '%s' should not end in 'Kind'"
- % (meta, name))
+ "%s '%s' should not end in '%s'"
+ % (meta, name, name[-4:]))
all_names[name] = meta
@@ -488,6 +490,10 @@ def check_type(expr_info, source, value, allow_array=False,
for (key, arg) in value.items():
check_name(expr_info, "Member of %s" % source, key,
allow_optional=allow_optional)
+ if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
+ raise QAPIExprError(expr_info,
+ "Member of %s uses reserved name '%s'"
+ % (source, key))
# Todo: allow dictionaries to represent default values of
# an optional argument.
check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
@@ -542,7 +548,8 @@ def check_union(expr, expr_info):
base = expr.get('base')
discriminator = expr.get('discriminator')
members = expr['data']
- values = {'MAX': '(automatic)', 'KIND': '(automatic)'}
+ values = {'MAX': '(automatic)', 'KIND': '(automatic)',
+ 'TYPE': '(automatic)'}
# Two types of unions, determined by discriminator.
@@ -910,7 +917,7 @@ class QAPISchemaEnumType(QAPISchemaType):
def is_implicit(self):
# See QAPISchema._make_implicit_enum_type()
- return self.name[-4:] == 'Kind'
+ return self.name.endswith('Kind')
def c_type(self, is_param=False):
return c_name(self.name)
@@ -1196,9 +1203,7 @@ class QAPISchema(object):
return name
def _make_array_type(self, element_type, info):
- # TODO fooList namespace is not reserved; user can create collisions,
- # or abuse our type system with ['fooList'] for 2D array
- name = element_type + 'List'
+ name = element_type + 'List' # Use namespace reserved by add_name()
if not self.lookup_type(name):
self._def_entity(QAPISchemaArrayType(name, info, element_type))
return name
diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py
index d6f2e5a..b3f8e04 100644
--- a/scripts/qemu-gdb.py
+++ b/scripts/qemu-gdb.py
@@ -26,7 +26,7 @@ import os, sys
sys.path.append(os.path.dirname(__file__))
-from qemugdb import mtree, coroutine
+from qemugdb import aio, mtree, coroutine
class QemuCommand(gdb.Command):
'''Prefix for QEMU debug support commands'''
@@ -37,6 +37,10 @@ class QemuCommand(gdb.Command):
QemuCommand()
coroutine.CoroutineCommand()
mtree.MtreeCommand()
+aio.HandlersCommand()
+
+coroutine.CoroutineSPFunction()
+coroutine.CoroutinePCFunction()
# Default to silently passing through SIGUSR1, because QEMU sends it
# to itself a lot.
diff --git a/scripts/qemugdb/aio.py b/scripts/qemugdb/aio.py
new file mode 100644
index 0000000..2ba00c4
--- /dev/null
+++ b/scripts/qemugdb/aio.py
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+
+# GDB debugging support: aio/iohandler debug
+#
+# Copyright (c) 2015 Red Hat, Inc.
+#
+# Author: Dr. David Alan Gilbert <dgilbert@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+#
+
+import gdb
+from qemugdb import coroutine
+
+def isnull(ptr):
+ return ptr == gdb.Value(0).cast(ptr.type)
+
+def dump_aiocontext(context, verbose):
+ '''Display a dump and backtrace for an aiocontext'''
+ cur = context['aio_handlers']['lh_first']
+ # Get pointers to functions we're going to process specially
+ sym_fd_coroutine_enter = gdb.parse_and_eval('fd_coroutine_enter')
+
+ while not isnull(cur):
+ entry = cur.dereference()
+ gdb.write('----\n%s\n' % entry)
+ if verbose and cur['io_read'] == sym_fd_coroutine_enter:
+ coptr = (cur['opaque'].cast(gdb.lookup_type('FDYieldUntilData').pointer()))['co']
+ coptr = coptr.cast(gdb.lookup_type('CoroutineUContext').pointer())
+ coroutine.bt_jmpbuf(coptr['env']['__jmpbuf'])
+ cur = cur['node']['le_next'];
+
+ gdb.write('----\n')
+
+class HandlersCommand(gdb.Command):
+ '''Display aio handlers'''
+ def __init__(self):
+ gdb.Command.__init__(self, 'qemu handlers', gdb.COMMAND_DATA,
+ gdb.COMPLETE_NONE)
+
+ def invoke(self, arg, from_tty):
+ verbose = False
+ argv = gdb.string_to_argv(arg)
+
+ if len(argv) > 0 and argv[0] == '--verbose':
+ verbose = True
+ argv.pop(0)
+
+ if len(argv) > 1:
+ gdb.write('usage: qemu handlers [--verbose] [handler]\n')
+ return
+
+ if len(argv) == 1:
+ handlers_name = argv[0]
+ else:
+ handlers_name = 'qemu_aio_context'
+ dump_aiocontext(gdb.parse_and_eval(handlers_name), verbose)
diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py
index 3c54918..ab69979 100644
--- a/scripts/qemugdb/coroutine.py
+++ b/scripts/qemugdb/coroutine.py
@@ -15,8 +15,11 @@
import gdb
+VOID_PTR = gdb.lookup_type('void').pointer()
+
def get_fs_base():
- '''Fetch %fs base value using arch_prctl(ARCH_GET_FS)'''
+ '''Fetch %fs base value using arch_prctl(ARCH_GET_FS). This is
+ pthread_self().'''
# %rsp - 120 is scratch space according to the SystemV ABI
old = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)')
gdb.execute('call arch_prctl(0x1003, $rsp - 120)', False, True)
@@ -24,17 +27,29 @@ def get_fs_base():
gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True)
return fs_base
+def pthread_self():
+ '''Fetch pthread_self() from the glibc start_thread function.'''
+ f = gdb.newest_frame()
+ while f.name() != 'start_thread':
+ f = f.older()
+ if f is None:
+ return get_fs_base()
+
+ try:
+ return f.read_var("arg")
+ except ValueError:
+ return get_fs_base()
+
def get_glibc_pointer_guard():
'''Fetch glibc pointer guard value'''
- fs_base = get_fs_base()
+ fs_base = pthread_self()
return gdb.parse_and_eval('*(uint64_t*)((uint64_t)%s + 0x30)' % fs_base)
def glibc_ptr_demangle(val, pointer_guard):
'''Undo effect of glibc's PTR_MANGLE()'''
return gdb.parse_and_eval('(((uint64_t)%s >> 0x11) | ((uint64_t)%s << (64 - 0x11))) ^ (uint64_t)%s' % (val, val, pointer_guard))
-def bt_jmpbuf(jmpbuf):
- '''Backtrace a jmpbuf'''
+def get_jmpbuf_regs(jmpbuf):
JB_RBX = 0
JB_RBP = 1
JB_R12 = 2
@@ -44,35 +59,35 @@ def bt_jmpbuf(jmpbuf):
JB_RSP = 6
JB_PC = 7
- old_rbx = gdb.parse_and_eval('(uint64_t)$rbx')
- old_rbp = gdb.parse_and_eval('(uint64_t)$rbp')
- old_rsp = gdb.parse_and_eval('(uint64_t)$rsp')
- old_r12 = gdb.parse_and_eval('(uint64_t)$r12')
- old_r13 = gdb.parse_and_eval('(uint64_t)$r13')
- old_r14 = gdb.parse_and_eval('(uint64_t)$r14')
- old_r15 = gdb.parse_and_eval('(uint64_t)$r15')
- old_rip = gdb.parse_and_eval('(uint64_t)$rip')
-
pointer_guard = get_glibc_pointer_guard()
- gdb.execute('set $rbx = %s' % jmpbuf[JB_RBX])
- gdb.execute('set $rbp = %s' % glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard))
- gdb.execute('set $rsp = %s' % glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard))
- gdb.execute('set $r12 = %s' % jmpbuf[JB_R12])
- gdb.execute('set $r13 = %s' % jmpbuf[JB_R13])
- gdb.execute('set $r14 = %s' % jmpbuf[JB_R14])
- gdb.execute('set $r15 = %s' % jmpbuf[JB_R15])
- gdb.execute('set $rip = %s' % glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard))
+ return {'rbx': jmpbuf[JB_RBX],
+ 'rbp': glibc_ptr_demangle(jmpbuf[JB_RBP], pointer_guard),
+ 'rsp': glibc_ptr_demangle(jmpbuf[JB_RSP], pointer_guard),
+ 'r12': jmpbuf[JB_R12],
+ 'r13': jmpbuf[JB_R13],
+ 'r14': jmpbuf[JB_R14],
+ 'r15': jmpbuf[JB_R15],
+ 'rip': glibc_ptr_demangle(jmpbuf[JB_PC], pointer_guard) }
+
+def bt_jmpbuf(jmpbuf):
+ '''Backtrace a jmpbuf'''
+ regs = get_jmpbuf_regs(jmpbuf)
+ old = dict()
+
+ for i in regs:
+ old[i] = gdb.parse_and_eval('(uint64_t)$%s' % i)
+
+ for i in regs:
+ gdb.execute('set $%s = %s' % (i, regs[i]))
gdb.execute('bt')
- gdb.execute('set $rbx = %s' % old_rbx)
- gdb.execute('set $rbp = %s' % old_rbp)
- gdb.execute('set $rsp = %s' % old_rsp)
- gdb.execute('set $r12 = %s' % old_r12)
- gdb.execute('set $r13 = %s' % old_r13)
- gdb.execute('set $r14 = %s' % old_r14)
- gdb.execute('set $r15 = %s' % old_r15)
- gdb.execute('set $rip = %s' % old_rip)
+ for i in regs:
+ gdb.execute('set $%s = %s' % (i, old[i]))
+
+def coroutine_to_jmpbuf(co):
+ coroutine_pointer = co.cast(gdb.lookup_type('CoroutineUContext').pointer())
+ return coroutine_pointer['env']['__jmpbuf']
class CoroutineCommand(gdb.Command):
@@ -87,5 +102,18 @@ class CoroutineCommand(gdb.Command):
gdb.write('usage: qemu coroutine <coroutine-pointer>\n')
return
- coroutine_pointer = gdb.parse_and_eval(argv[0]).cast(gdb.lookup_type('CoroutineUContext').pointer())
- bt_jmpbuf(coroutine_pointer['env']['__jmpbuf'])
+ bt_jmpbuf(coroutine_to_jmpbuf(gdb.parse_and_eval(argv[0])))
+
+class CoroutineSPFunction(gdb.Function):
+ def __init__(self):
+ gdb.Function.__init__(self, 'qemu_coroutine_sp')
+
+ def invoke(self, addr):
+ return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rsp'].cast(VOID_PTR)
+
+class CoroutinePCFunction(gdb.Function):
+ def __init__(self):
+ gdb.Function.__init__(self, 'qemu_coroutine_pc')
+
+ def invoke(self, addr):
+ return get_jmpbuf_regs(coroutine_to_jmpbuf(addr))['rip'].cast(VOID_PTR)