diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/qapi-commands.py | 109 | ||||
-rw-r--r-- | scripts/qapi-event.py | 6 | ||||
-rw-r--r-- | scripts/qapi-types.py | 110 | ||||
-rw-r--r-- | scripts/qapi-visit.py | 93 | ||||
-rw-r--r-- | scripts/qapi.py | 79 | ||||
-rw-r--r-- | scripts/qemu-gdb.py | 149 | ||||
-rw-r--r-- | scripts/qemugdb/__init__.py | 28 | ||||
-rw-r--r-- | scripts/qemugdb/coroutine.py | 91 | ||||
-rw-r--r-- | scripts/qemugdb/mtree.py | 82 | ||||
-rwxr-xr-x | scripts/qmp/qmp-shell | 35 |
10 files changed, 454 insertions, 328 deletions
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index ca22acc1d5..890ce5db92 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -27,18 +27,19 @@ def generate_command_decl(name, args, ret_type): %(ret_type)s qmp_%(name)s(%(args)sError **errp); ''', ret_type=c_type(ret_type), name=c_name(name), - args=arglist).strip() + args=arglist) -def gen_err_check(errvar): - if errvar: - return mcgen(''' -if (local_err) { +def gen_err_check(err): + if not err: + return '' + return mcgen(''' +if (%(err)s) { goto out; } -''') - return '' +''', + err=err) -def gen_sync_call(name, args, ret_type, indent=0): +def gen_sync_call(name, args, ret_type): ret = "" arglist="" retval="" @@ -48,41 +49,34 @@ def gen_sync_call(name, args, ret_type, indent=0): if optional: arglist += "has_%s, " % c_name(argname) arglist += "%s, " % (c_name(argname)) - push_indent(indent) + push_indent() ret = mcgen(''' %(retval)sqmp_%(name)s(%(args)s&local_err); - ''', - name=c_name(name), args=arglist, retval=retval).rstrip() + name=c_name(name), args=arglist, retval=retval) if ret_type: - ret += "\n" + gen_err_check('local_err') - ret += "\n" + mcgen('''' -%(marshal_output_call)s -''', - marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() - pop_indent(indent) - return ret.rstrip() - + ret += gen_err_check('local_err') + ret += mcgen(''' -def gen_marshal_output_call(name, ret_type): - if not ret_type: - return "" - return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_name(name) +qmp_marshal_output_%(c_name)s(retval, ret, &local_err); +''', + c_name=c_name(name)) + pop_indent() + return ret -def gen_visitor_input_containers_decl(args, obj): +def gen_visitor_input_containers_decl(args): ret = "" push_indent() if len(args) > 0: ret += mcgen(''' -QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s); +QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); QapiDeallocVisitor *md; Visitor *v; -''', - obj=obj) +''') pop_indent() - return ret.rstrip() + return ret def gen_visitor_input_vars_decl(args): ret = "" @@ -105,7 +99,7 @@ bool has_%(argname)s = false; argname=c_name(argname), argtype=c_type(argtype)) pop_indent() - return ret.rstrip() + return ret def gen_visitor_input_block(args, dealloc=False): ret = "" @@ -159,9 +153,9 @@ visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); qapi_dealloc_visitor_cleanup(md); ''') pop_indent() - return ret.rstrip() + return ret -def gen_marshal_output(name, args, ret_type, middle_mode): +def gen_marshal_output(name, ret_type): if not ret_type: return "" @@ -194,14 +188,14 @@ out: return ret -def gen_marshal_input_decl(name, args, ret_type, middle_mode): +def gen_marshal_input_decl(name, middle_mode): ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) if not middle_mode: ret = "static " + ret return ret def gen_marshal_input(name, args, ret_type, middle_mode): - hdr = gen_marshal_input_decl(name, args, ret_type, middle_mode) + hdr = gen_marshal_input_decl(name, middle_mode) ret = mcgen(''' %(header)s @@ -211,36 +205,24 @@ def gen_marshal_input(name, args, ret_type, middle_mode): header=hdr) if ret_type: - if is_c_ptr(ret_type): - retval = " %s retval = NULL;" % c_type(ret_type) - else: - retval = " %s retval;" % c_type(ret_type) ret += mcgen(''' -%(retval)s + %(c_type)s retval; ''', - retval=retval) + c_type=c_type(ret_type)) if len(args) > 0: - ret += mcgen(''' -%(visitor_input_containers_decl)s -%(visitor_input_vars_decl)s - -%(visitor_input_block)s - -''', - visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"), - visitor_input_vars_decl=gen_visitor_input_vars_decl(args), - visitor_input_block=gen_visitor_input_block(args)) + ret += gen_visitor_input_containers_decl(args) + ret += gen_visitor_input_vars_decl(args) + '\n' + ret += gen_visitor_input_block(args) + '\n' else: ret += mcgen(''' (void)args; + ''') - ret += mcgen(''' -%(sync_call)s -''', - sync_call=gen_sync_call(name, args, ret_type, indent=4)) + ret += gen_sync_call(name, args, ret_type) + if re.search('^ *goto out\\;', ret, re.MULTILINE): ret += mcgen(''' @@ -248,11 +230,11 @@ out: ''') ret += mcgen(''' error_propagate(errp, local_err); -%(visitor_input_block_cleanup)s +''') + ret += gen_visitor_input_block(args, dealloc=True) + ret += mcgen(''' } -''', - visitor_input_block_cleanup=gen_visitor_input_block(args, - dealloc=True)) +''') return ret def gen_registry(commands): @@ -272,12 +254,13 @@ qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); ret = mcgen(''' static void qmp_init_marshal(void) { -%(registry)s +''') + ret += registry + ret += mcgen(''' } qapi_init(qmp_init_marshal); -''', - registry=registry.rstrip()) +''') return ret middle_mode = False @@ -357,14 +340,14 @@ for cmd in commands: arglist = cmd['data'] if cmd.has_key('returns'): ret_type = cmd['returns'] - ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" + ret = generate_command_decl(cmd['command'], arglist, ret_type) fdecl.write(ret) if ret_type: - ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" + ret = gen_marshal_output(cmd['command'], ret_type) + "\n" fdef.write(ret) if middle_mode: - fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode)) + fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], middle_mode)) ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n" fdef.write(ret) diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index 56bc602a6d..7f238df5b2 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -167,8 +167,7 @@ extern const char *%(event_enum_name)s_lookup[]; event_enum_name = event_enum_name) enum_decl = mcgen(''' -typedef enum %(event_enum_name)s -{ +typedef enum %(event_enum_name)s { ''', event_enum_name = event_enum_name) @@ -199,7 +198,6 @@ const char *%(event_enum_name)s_lookup[] = { ''', event_enum_name = event_enum_name) - i = 0 for string in event_enum_strings: ret += mcgen(''' "%(string)s", @@ -267,7 +265,7 @@ fdecl.write(mcgen(''' exprs = parse_schema(input_file) -event_enum_name = prefix.upper().replace('-', '_') + "QAPIEvent" +event_enum_name = c_name(prefix + "QAPIEvent", protect=False) event_enum_values = [] event_enum_strings = [] diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index e6eb4b613a..f2428f3807 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -15,8 +15,7 @@ from qapi import * def generate_fwd_builtin(name): return mcgen(''' -typedef struct %(name)sList -{ +typedef struct %(name)sList { union { %(type)s value; uint64_t padding; @@ -32,8 +31,7 @@ def generate_fwd_struct(name): typedef struct %(name)s %(name)s; -typedef struct %(name)sList -{ +typedef struct %(name)sList { union { %(name)s *value; uint64_t padding; @@ -45,8 +43,8 @@ typedef struct %(name)sList def generate_fwd_enum_struct(name): return mcgen(''' -typedef struct %(name)sList -{ + +typedef struct %(name)sList { union { %(name)s value; uint64_t padding; @@ -79,8 +77,8 @@ def generate_struct(expr): base = expr.get('base') ret = mcgen(''' -struct %(name)s -{ + +struct %(name)s { ''', name=c_name(structname)) @@ -105,10 +103,10 @@ struct %(name)s def generate_enum_lookup(name, values): ret = mcgen(''' -const char * const %(name)s_lookup[] = { + +const char *const %(name)s_lookup[] = { ''', name=c_name(name)) - i = 0 for value in values: index = c_enum_const(name, value) ret += mcgen(''' @@ -120,7 +118,6 @@ const char * const %(name)s_lookup[] = { ret += mcgen(''' [%(max_index)s] = NULL, }; - ''', max_index=max_index) return ret @@ -128,13 +125,14 @@ const char * const %(name)s_lookup[] = { def generate_enum(name, values): name = c_name(name) lookup_decl = mcgen(''' -extern const char * const %(name)s_lookup[]; + +extern const char *const %(name)s_lookup[]; ''', name=name) enum_decl = mcgen(''' -typedef enum %(name)s -{ + +typedef enum %(name)s { ''', name=name) @@ -156,7 +154,7 @@ typedef enum %(name)s ''', name=name) - return lookup_decl + enum_decl + return enum_decl + lookup_decl def generate_alternate_qtypes(expr): @@ -164,6 +162,7 @@ def generate_alternate_qtypes(expr): members = expr['data'] ret = mcgen(''' + const int %(name)s_qtypes[QTYPE_MAX] = { ''', name=c_name(name)) @@ -199,14 +198,40 @@ def generate_union(expr, meta): discriminator_type_name = '%sKind' % (name) ret = mcgen(''' -struct %(name)s -{ + +struct %(name)s { +''', + name=name) + if base: + ret += mcgen(''' + /* Members inherited from %(c_name)s: */ +''', + c_name=c_name(base)) + base_fields = find_struct(base)['data'] + ret += generate_struct_fields(base_fields) + ret += mcgen(''' + /* Own members: */ +''') + else: + assert not discriminator + ret += mcgen(''' %(discriminator_type_name)s kind; - union { +''', + discriminator_type_name=c_name(discriminator_type_name)) + + # 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 + # whether to bypass the switch statement if visiting the discriminator + # failed; but since we 0-initialize structs, and cannot tell what + # branch of the union is in use if the discriminator is invalid, there + # should not be any data leaks even without a data pointer. Or, if + # 'data' is merely added to guarantee we don't have an empty union, + # shouldn't we enforce that at .json parse time? + ret += mcgen(''' + union { /* union tag is @%(c_name)s */ void *data; ''', - name=name, - discriminator_type_name=c_name(discriminator_type_name)) + c_name=c_name(discriminator or 'kind')) for key in typeinfo: ret += mcgen(''' @@ -217,17 +242,6 @@ struct %(name)s ret += mcgen(''' }; -''') - - if base: - assert discriminator - base_fields = find_struct(base)['data'].copy() - del base_fields[discriminator] - ret += generate_struct_fields(base_fields) - else: - assert not discriminator - - ret += mcgen(''' }; ''') if meta == 'alternate': @@ -314,14 +328,12 @@ fdef.write(mcgen(''' #include "qapi/dealloc-visitor.h" #include "%(prefix)sqapi-types.h" #include "%(prefix)sqapi-visit.h" - ''', prefix=prefix)) fdecl.write(mcgen(''' #include <stdbool.h> #include <stdint.h> - ''')) exprs = parse_schema(input_file) @@ -332,22 +344,22 @@ for typename in builtin_types.keys(): fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL")) for expr in exprs: - ret = "\n" + ret = "" if expr.has_key('struct'): ret += generate_fwd_struct(expr['struct']) elif expr.has_key('enum'): - ret += generate_enum(expr['enum'], expr['data']) + "\n" + ret += generate_enum(expr['enum'], expr['data']) ret += generate_fwd_enum_struct(expr['enum']) fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) elif expr.has_key('union'): - ret += generate_fwd_struct(expr['union']) + "\n" + ret += generate_fwd_struct(expr['union']) enum_define = discriminator_find_enum_define(expr) if not enum_define: ret += generate_enum('%sKind' % expr['union'], expr['data'].keys()) fdef.write(generate_enum_lookup('%sKind' % expr['union'], expr['data'].keys())) elif expr.has_key('alternate'): - ret += generate_fwd_struct(expr['alternate']) + "\n" + ret += generate_fwd_struct(expr['alternate']) ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys()) fdef.write(generate_enum_lookup('%sKind' % expr['alternate'], expr['data'].keys())) @@ -367,34 +379,32 @@ fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL")) # have the functions defined, so we use -b option to provide control # over these cases if do_builtins: - fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF")) for typename in builtin_types.keys(): fdef.write(generate_type_cleanup(typename + "List")) - fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF")) for expr in exprs: - ret = "\n" + ret = "" if expr.has_key('struct'): ret += generate_struct(expr) + "\n" ret += generate_type_cleanup_decl(expr['struct'] + "List") - fdef.write(generate_type_cleanup(expr['struct'] + "List") + "\n") + fdef.write(generate_type_cleanup(expr['struct'] + "List")) ret += generate_type_cleanup_decl(expr['struct']) - fdef.write(generate_type_cleanup(expr['struct']) + "\n") + fdef.write(generate_type_cleanup(expr['struct'])) elif expr.has_key('union'): - ret += generate_union(expr, 'union') + ret += generate_union(expr, 'union') + "\n" ret += generate_type_cleanup_decl(expr['union'] + "List") - fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n") + fdef.write(generate_type_cleanup(expr['union'] + "List")) ret += generate_type_cleanup_decl(expr['union']) - fdef.write(generate_type_cleanup(expr['union']) + "\n") + fdef.write(generate_type_cleanup(expr['union'])) elif expr.has_key('alternate'): - ret += generate_union(expr, 'alternate') + ret += generate_union(expr, 'alternate') + "\n" ret += generate_type_cleanup_decl(expr['alternate'] + "List") - fdef.write(generate_type_cleanup(expr['alternate'] + "List") + "\n") + fdef.write(generate_type_cleanup(expr['alternate'] + "List")) ret += generate_type_cleanup_decl(expr['alternate']) - fdef.write(generate_type_cleanup(expr['alternate']) + "\n") + fdef.write(generate_type_cleanup(expr['alternate'])) elif expr.has_key('enum'): - ret += generate_type_cleanup_decl(expr['enum'] + "List") - fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n") + ret += "\n" + generate_type_cleanup_decl(expr['enum'] + "List") + fdef.write(generate_type_cleanup(expr['enum'] + "List")) else: continue fdecl.write(ret) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 5b99336488..3cd662bd6b 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -16,14 +16,23 @@ from ordereddict import OrderedDict from qapi import * import re -implicit_structs = [] +implicit_structs_seen = set() +struct_fields_seen = set() def generate_visit_implicit_struct(type): - global implicit_structs - if type in implicit_structs: + if type in implicit_structs_seen: return '' - implicit_structs.append(type) - return mcgen(''' + implicit_structs_seen.add(type) + ret = '' + if type not in struct_fields_seen: + # Need a forward declaration + ret += mcgen(''' + +static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp); +''', + c_type=type_name(type)) + + ret += mcgen(''' static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp) { @@ -38,9 +47,11 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error * } ''', c_type=type_name(type)) + return ret def generate_visit_struct_fields(name, members, base = None): - substructs = [] + struct_fields_seen.add(name) + ret = '' if base: @@ -51,6 +62,7 @@ def generate_visit_struct_fields(name, members, base = None): static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp) { Error *err = NULL; + ''', name=c_name(name)) push_indent() @@ -103,7 +115,11 @@ out: return ret -def generate_visit_struct_body(name, members): +def generate_visit_struct_body(name): + # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to + # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj + # rather than leaving it non-NULL. As currently written, the caller must + # call qapi_free_FOO() to avoid a memory leak of the partial FOO. ret = mcgen(''' Error *err = NULL; @@ -135,14 +151,14 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e ''', name=c_name(name)) - ret += generate_visit_struct_body(name, members) + ret += generate_visit_struct_body(name) ret += mcgen(''' } ''') return ret -def generate_visit_list(name, members): +def generate_visit_list(name): return mcgen(''' void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp) @@ -171,15 +187,15 @@ out: ''', name=type_name(name)) -def generate_visit_enum(name, members): +def generate_visit_enum(name): return mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp) { - visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp); + visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp); } ''', - name=c_name(name)) + c_name=c_name(name), name=name) def generate_visit_alternate(name, members): ret = mcgen(''' @@ -252,7 +268,7 @@ def generate_visit_union(expr): else: # There will always be a discriminator in the C switch code, by default # it is an enum type generated silently - ret = generate_visit_enum(name + 'Kind', members.keys()) + ret = generate_visit_enum(name + 'Kind') disc_type = c_name(name) + 'Kind' if base: @@ -267,17 +283,17 @@ def generate_visit_union(expr): ret += mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp) { Error *err = NULL; - visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); + visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err); if (err) { goto out; } if (*obj) { ''', - name=c_name(name)) + c_name=c_name(name), name=name) if base: ret += mcgen(''' @@ -289,20 +305,23 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e name=c_name(name)) if not discriminator: + tag = 'kind' disc_key = "type" else: + tag = discriminator disc_key = discriminator ret += mcgen(''' - visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); + visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err); if (err) { goto out_obj; } if (!visit_start_union(m, !!(*obj)->data, &err) || err) { goto out_obj; } - switch ((*obj)->kind) { + switch ((*obj)->%(c_tag)s) { ''', disc_type = disc_type, + c_tag=c_name(tag), disc_key = disc_key) for key in members: @@ -340,7 +359,7 @@ out: return ret -def generate_declaration(name, members, builtin_type=False): +def generate_declaration(name, builtin_type=False): ret = "" if not builtin_type: name = c_name(name) @@ -357,7 +376,7 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E return ret -def generate_enum_declaration(name, members): +def generate_enum_declaration(name): ret = mcgen(''' void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); ''', @@ -365,7 +384,7 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E return ret -def generate_decl_enum(name, members): +def generate_decl_enum(name): return mcgen(''' void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp); @@ -433,7 +452,7 @@ exprs = parse_schema(input_file) # for built-in types in our header files and simply guard them fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL")) for typename in builtin_types.keys(): - fdecl.write(generate_declaration(typename, None, builtin_type=True)) + fdecl.write(generate_declaration(typename, builtin_type=True)) fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) # ...this doesn't work for cases where we link in multiple objects that @@ -441,44 +460,42 @@ fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) # over these cases if do_builtins: for typename in builtin_types.keys(): - fdef.write(generate_visit_list(typename, None)) + fdef.write(generate_visit_list(typename)) for expr in exprs: if expr.has_key('struct'): ret = generate_visit_struct(expr) - ret += generate_visit_list(expr['struct'], expr['data']) + ret += generate_visit_list(expr['struct']) fdef.write(ret) - ret = generate_declaration(expr['struct'], expr['data']) + ret = generate_declaration(expr['struct']) fdecl.write(ret) elif expr.has_key('union'): ret = generate_visit_union(expr) - ret += generate_visit_list(expr['union'], expr['data']) + ret += generate_visit_list(expr['union']) fdef.write(ret) enum_define = discriminator_find_enum_define(expr) ret = "" if not enum_define: - ret = generate_decl_enum('%sKind' % expr['union'], - expr['data'].keys()) - ret += generate_declaration(expr['union'], expr['data']) + ret = generate_decl_enum('%sKind' % expr['union']) + ret += generate_declaration(expr['union']) fdecl.write(ret) elif expr.has_key('alternate'): ret = generate_visit_alternate(expr['alternate'], expr['data']) - ret += generate_visit_list(expr['alternate'], expr['data']) + ret += generate_visit_list(expr['alternate']) fdef.write(ret) - ret = generate_decl_enum('%sKind' % expr['alternate'], - expr['data'].keys()) - ret += generate_declaration(expr['alternate'], expr['data']) + ret = generate_decl_enum('%sKind' % expr['alternate']) + ret += generate_declaration(expr['alternate']) fdecl.write(ret) elif expr.has_key('enum'): - ret = generate_visit_list(expr['enum'], expr['data']) - ret += generate_visit_enum(expr['enum'], expr['data']) + ret = generate_visit_list(expr['enum']) + ret += generate_visit_enum(expr['enum']) fdef.write(ret) - ret = generate_decl_enum(expr['enum'], expr['data']) - ret += generate_enum_declaration(expr['enum'], expr['data']) + ret = generate_decl_enum(expr['enum']) + ret += generate_enum_declaration(expr['enum']) fdecl.write(ret) close_output(fdef, fdecl) diff --git a/scripts/qapi.py b/scripts/qapi.py index 06d7fc2848..817d824bea 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -341,6 +341,8 @@ def discriminator_find_enum_define(expr): return find_enum(discriminator_type) +# FIXME should enforce "other than downstream extensions [...], all +# names should begin with a letter". valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$') def check_name(expr_info, source, name, allow_optional = False, enum_member = False): @@ -367,6 +369,8 @@ def check_name(expr_info, source, name, allow_optional = False, def add_name(name, info, meta, implicit = False): global all_names check_name(info, "'%s'" % meta, name) + # FIXME should reject names that differ only in '_' vs. '.' + # vs. '-', because they're liable to clash in generated C. if name in all_names: raise QAPIExprError(info, "%s '%s' is already defined" @@ -422,7 +426,6 @@ def check_type(expr_info, source, value, allow_array = False, allow_dict = False, allow_optional = False, allow_star = False, allow_metas = []): global all_names - orig_value = value if value is None: return @@ -440,7 +443,6 @@ def check_type(expr_info, source, value, allow_array = False, "%s: array type must contain single type name" % source) value = value[0] - orig_value = "array of %s" %value # Check if type name for value is okay if isinstance(value, str): @@ -451,20 +453,22 @@ def check_type(expr_info, source, value, allow_array = False, if not value in all_names: raise QAPIExprError(expr_info, "%s uses unknown type '%s'" - % (source, orig_value)) + % (source, value)) if not all_names[value] in allow_metas: raise QAPIExprError(expr_info, "%s cannot use %s type '%s'" - % (source, all_names[value], orig_value)) + % (source, all_names[value], value)) return - # value is a dictionary, check that each member is okay - if not isinstance(value, OrderedDict): - raise QAPIExprError(expr_info, - "%s should be a dictionary" % source) if not allow_dict: raise QAPIExprError(expr_info, "%s should be a type name" % source) + + if not isinstance(value, OrderedDict): + raise QAPIExprError(expr_info, + "%s should be a dictionary or type name" % source) + + # value is a dictionary, check that each member is okay for (key, arg) in value.items(): check_name(expr_info, "Member of %s" % source, key, allow_optional=allow_optional) @@ -495,26 +499,25 @@ def check_command(expr, expr_info): check_type(expr_info, "'data' for command '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['union', 'struct'], allow_star=allow_star) + allow_metas=['struct'], allow_star=allow_star) returns_meta = ['union', 'struct'] if name in returns_whitelist: returns_meta += ['built-in', 'alternate', 'enum'] check_type(expr_info, "'returns' for command '%s'" % name, - expr.get('returns'), allow_array=True, allow_dict=True, + expr.get('returns'), allow_array=True, allow_optional=True, allow_metas=returns_meta, allow_star=allow_star) def check_event(expr, expr_info): global events name = expr['event'] - params = expr.get('data') if name.upper() == 'MAX': raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created") events.append(name) check_type(expr_info, "'data' for event '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['union', 'struct']) + allow_metas=['struct']) def check_union(expr, expr_info): name = expr['union'] @@ -523,14 +526,6 @@ def check_union(expr, expr_info): members = expr['data'] values = { 'MAX': '(automatic)' } - # If the object has a member 'base', its value must name a struct, - # and there must be a discriminator. - if base is not None: - if discriminator is None: - raise QAPIExprError(expr_info, - "Union '%s' requires a discriminator to go " - "along with base" %name) - # Two types of unions, determined by discriminator. # With no discriminator it is a simple union. @@ -943,24 +938,24 @@ def pop_indent(indent_amount=4): global indent_level indent_level -= indent_amount +# Generate @code with @kwds interpolated. +# Obey indent_level, and strip eatspace. def cgen(code, **kwds): - indent = genindent(indent_level) - lines = code.split('\n') - lines = map(lambda x: indent + x, lines) - return '\n'.join(lines) % kwds + '\n' + raw = code % kwds + if indent_level: + indent = genindent(indent_level) + raw = re.subn("^.", indent + r'\g<0>', raw, 0, re.MULTILINE) + raw = raw[0] + return re.sub(re.escape(eatspace) + ' *', '', raw) def mcgen(code, **kwds): - raw = cgen('\n'.join(code.split('\n')[1:-1]), **kwds) - return re.sub(re.escape(eatspace) + ' *', '', raw) + if code[0] == '\n': + code = code[1:] + return cgen(code, **kwds) -def basename(filename): - return filename.split("/")[-1] def guardname(filename): - guard = basename(filename).rsplit(".", 1)[0] - for substr in [".", " ", "-"]: - guard = guard.replace(substr, "_") - return guard.upper() + '_H' + return c_name(filename, protect=False).upper() def guardstart(name): return mcgen(''' @@ -1003,6 +998,12 @@ def parse_command_line(extra_options = "", extra_long_options = []): for oa in opts: o, a = oa if o in ("-p", "--prefix"): + match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) + if match.end() != len(a): + print >>sys.stderr, \ + "%s: 'funny character '%s' in argument of --prefix" \ + % (sys.argv[0], a[match.end()]) + sys.exit(1) prefix = a elif o in ("-o", "--output-dir"): output_dir = a + "/" @@ -1030,14 +1031,16 @@ def parse_command_line(extra_options = "", extra_long_options = []): def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, c_comment, h_comment): + guard = guardname(prefix + h_file) c_file = output_dir + prefix + c_file h_file = output_dir + prefix + h_file - try: - os.makedirs(output_dir) - except os.error, e: - if e.errno != errno.EEXIST: - raise + if output_dir: + try: + os.makedirs(output_dir) + except os.error, e: + if e.errno != errno.EEXIST: + raise def maybe_open(really, name, opt): if really: @@ -1062,7 +1065,7 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, #define %(guard)s ''', - comment = h_comment, guard = guardname(h_file))) + comment = h_comment, guard = guard)) return (fdef, fdecl) diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py index 6c7f4fbe53..d6f2e5a903 100644 --- a/scripts/qemu-gdb.py +++ b/scripts/qemu-gdb.py @@ -13,73 +13,20 @@ # Contributions after 2012-01-13 are licensed under the terms of the # GNU GPL, version 2 or (at your option) any later version. +# Usage: +# At the (gdb) prompt, type "source scripts/qemu-gdb.py". +# "help qemu" should then list the supported QEMU debug support commands. import gdb -def isnull(ptr): - return ptr == gdb.Value(0).cast(ptr.type) +import os, sys -def int128(p): - return long(p['lo']) + (long(p['hi']) << 64) +# Annoyingly, gdb doesn't put the directory of scripts onto the +# module search path. Do it manually. -def get_fs_base(): - '''Fetch %fs base value using arch_prctl(ARCH_GET_FS)''' - # %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) - fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') - gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) - return fs_base +sys.path.append(os.path.dirname(__file__)) -def get_glibc_pointer_guard(): - '''Fetch glibc pointer guard value''' - fs_base = get_fs_base() - 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''' - JB_RBX = 0 - JB_RBP = 1 - JB_R12 = 2 - JB_R13 = 3 - JB_R14 = 4 - JB_R15 = 5 - 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)) - - 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) +from qemugdb import mtree, coroutine class QemuCommand(gdb.Command): '''Prefix for QEMU debug support commands''' @@ -87,78 +34,10 @@ class QemuCommand(gdb.Command): gdb.Command.__init__(self, 'qemu', gdb.COMMAND_DATA, gdb.COMPLETE_NONE, True) -class CoroutineCommand(gdb.Command): - '''Display coroutine backtrace''' - def __init__(self): - gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA, - gdb.COMPLETE_NONE) - - def invoke(self, arg, from_tty): - argv = gdb.string_to_argv(arg) - if len(argv) != 1: - 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']) - -class MtreeCommand(gdb.Command): - '''Display the memory tree hierarchy''' - def __init__(self): - gdb.Command.__init__(self, 'qemu mtree', gdb.COMMAND_DATA, - gdb.COMPLETE_NONE) - self.queue = [] - def invoke(self, arg, from_tty): - self.seen = set() - self.queue_root('address_space_memory') - self.queue_root('address_space_io') - self.process_queue() - def queue_root(self, varname): - ptr = gdb.parse_and_eval(varname)['root'] - self.queue.append(ptr) - def process_queue(self): - while self.queue: - ptr = self.queue.pop(0) - if long(ptr) in self.seen: - continue - self.print_item(ptr) - def print_item(self, ptr, offset = gdb.Value(0), level = 0): - self.seen.add(long(ptr)) - addr = ptr['addr'] - addr += offset - size = int128(ptr['size']) - alias = ptr['alias'] - klass = '' - if not isnull(alias): - klass = ' (alias)' - elif not isnull(ptr['ops']): - klass = ' (I/O)' - elif bool(ptr['ram']): - klass = ' (RAM)' - gdb.write('%s%016x-%016x %s%s (@ %s)\n' - % (' ' * level, - long(addr), - long(addr + (size - 1)), - ptr['name'].string(), - klass, - ptr, - ), - gdb.STDOUT) - if not isnull(alias): - gdb.write('%s alias: %s@%016x (@ %s)\n' % - (' ' * level, - alias['name'].string(), - ptr['alias_offset'], - alias, - ), - gdb.STDOUT) - self.queue.append(alias) - subregion = ptr['subregions']['tqh_first'] - level += 1 - while not isnull(subregion): - self.print_item(subregion, addr, level) - subregion = subregion['subregions_link']['tqe_next'] - QemuCommand() -CoroutineCommand() -MtreeCommand() +coroutine.CoroutineCommand() +mtree.MtreeCommand() + +# Default to silently passing through SIGUSR1, because QEMU sends it +# to itself a lot. +gdb.execute('handle SIGUSR1 pass noprint nostop') diff --git a/scripts/qemugdb/__init__.py b/scripts/qemugdb/__init__.py new file mode 100644 index 0000000000..969f552b26 --- /dev/null +++ b/scripts/qemugdb/__init__.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright (c) 2015 Linaro Ltd +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see +# <http://www.gnu.org/licenses/gpl-2.0.html> +# + +# We don't need to do anything in our init file currently. + +""" +Support routines for debugging QEMU under GDB +""" + +__license__ = "GPL version 2 or (at your option) any later version" diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py new file mode 100644 index 0000000000..3c54918b5d --- /dev/null +++ b/scripts/qemugdb/coroutine.py @@ -0,0 +1,91 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright 2012 Red Hat, Inc. and/or its affiliates +# +# Authors: +# Avi Kivity <avi@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Contributions after 2012-01-13 are licensed under the terms of the +# GNU GPL, version 2 or (at your option) any later version. + +import gdb + +def get_fs_base(): + '''Fetch %fs base value using arch_prctl(ARCH_GET_FS)''' + # %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) + fs_base = gdb.parse_and_eval('*(uint64_t*)($rsp - 120)') + gdb.execute('set *(uint64_t*)($rsp - 120) = %s' % old, False, True) + return fs_base + +def get_glibc_pointer_guard(): + '''Fetch glibc pointer guard value''' + fs_base = get_fs_base() + 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''' + JB_RBX = 0 + JB_RBP = 1 + JB_R12 = 2 + JB_R13 = 3 + JB_R14 = 4 + JB_R15 = 5 + 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)) + + 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) + + +class CoroutineCommand(gdb.Command): + '''Display coroutine backtrace''' + def __init__(self): + gdb.Command.__init__(self, 'qemu coroutine', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + argv = gdb.string_to_argv(arg) + if len(argv) != 1: + 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']) diff --git a/scripts/qemugdb/mtree.py b/scripts/qemugdb/mtree.py new file mode 100644 index 0000000000..06011c30cc --- /dev/null +++ b/scripts/qemugdb/mtree.py @@ -0,0 +1,82 @@ +#!/usr/bin/python + +# GDB debugging support +# +# Copyright 2012 Red Hat, Inc. and/or its affiliates +# +# Authors: +# Avi Kivity <avi@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Contributions after 2012-01-13 are licensed under the terms of the +# GNU GPL, version 2 or (at your option) any later version. + +# 'qemu mtree' -- display the memory hierarchy + +import gdb + +def isnull(ptr): + return ptr == gdb.Value(0).cast(ptr.type) + +def int128(p): + return long(p['lo']) + (long(p['hi']) << 64) + +class MtreeCommand(gdb.Command): + '''Display the memory tree hierarchy''' + def __init__(self): + gdb.Command.__init__(self, 'qemu mtree', gdb.COMMAND_DATA, + gdb.COMPLETE_NONE) + self.queue = [] + def invoke(self, arg, from_tty): + self.seen = set() + self.queue_root('address_space_memory') + self.queue_root('address_space_io') + self.process_queue() + def queue_root(self, varname): + ptr = gdb.parse_and_eval(varname)['root'] + self.queue.append(ptr) + def process_queue(self): + while self.queue: + ptr = self.queue.pop(0) + if long(ptr) in self.seen: + continue + self.print_item(ptr) + def print_item(self, ptr, offset = gdb.Value(0), level = 0): + self.seen.add(long(ptr)) + addr = ptr['addr'] + addr += offset + size = int128(ptr['size']) + alias = ptr['alias'] + klass = '' + if not isnull(alias): + klass = ' (alias)' + elif not isnull(ptr['ops']): + klass = ' (I/O)' + elif bool(ptr['ram']): + klass = ' (RAM)' + gdb.write('%s%016x-%016x %s%s (@ %s)\n' + % (' ' * level, + long(addr), + long(addr + (size - 1)), + ptr['name'].string(), + klass, + ptr, + ), + gdb.STDOUT) + if not isnull(alias): + gdb.write('%s alias: %s@%016x (@ %s)\n' % + (' ' * level, + alias['name'].string(), + ptr['alias_offset'], + alias, + ), + gdb.STDOUT) + self.queue.append(alias) + subregion = ptr['subregions']['tqh_first'] + level += 1 + while not isnull(subregion): + self.print_item(subregion, addr, level) + subregion = subregion['subregions_link']['tqe_next'] + diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index 65280d29d1..fa39bf0d7b 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -29,6 +29,41 @@ # (QEMU) device_add driver=e1000 id=net1 # {u'return': {}} # (QEMU) +# +# key=value pairs also support Python or JSON object literal subset notations, +# without spaces. Dictionaries/objects {} are supported as are arrays []. +# +# example-command arg-name1={'key':'value','obj'={'prop':"value"}} +# +# Both JSON and Python formatting should work, including both styles of +# string literal quotes. Both paradigms of literal values should work, +# including null/true/false for JSON and None/True/False for Python. +# +# +# Transactions have the following multi-line format: +# +# transaction( +# action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ] +# ... +# action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ] +# ) +# +# One line transactions are also supported: +# +# transaction( action-name1 ... ) +# +# For example: +# +# (QEMU) transaction( +# TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1 +# TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0 +# TRANS> ) +# {"return": {}} +# (QEMU) +# +# Use the -v and -p options to activate the verbose and pretty-print options, +# which will echo back the properly formatted JSON-compliant QMP that is being +# sent to QEMU, which is useful for debugging and documentation generation. import qmp import json |