This is about QOM use. Cc: Andreas in case he has smart ideas. Andreas, you may want to skip ahead to "EnumProperty".
"Lin Ma" <l...@suse.com> writes: >>>> Markus Armbruster <arm...@redhat.com> 2016/9/12 星期一 下午 11:42 >>> >>Lin Ma <l...@suse.com> writes: >> >>> '-object help' prints available user creatable backends. >>> '-object $typename,help' prints relevant properties. >>> >>> Signed-off-by: Lin Ma <l...@suse.com> >>> --- >>> include/qom/object_interfaces.h | 2 + >>> qemu-options.hx | 7 ++- >>> qom/object_interfaces.c | 112 ++++++++++++++++++++++++++++++++++++++++ >>> vl.c | 5 ++ >>> 4 files changed, 125 insertions(+), 1 deletion(-) >>> [...] >>> diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c >>> index bf59846..4ee8643 100644 >>> --- a/qom/object_interfaces.c >>> +++ b/qom/object_interfaces.c >>> @@ -5,6 +5,7 @@ >>> #include "qapi-visit.h" >>> #include "qapi/qmp-output-visitor.h" >>> #include "qapi/opts-visitor.h" >>> +#include "qemu/help_option.h" >>> >>> void user_creatable_complete(Object *obj, Error **errp) >>> { >>> @@ -212,6 +213,117 @@ void user_creatable_del(const char *id, Error **errp) >>> object_unparent(obj); >>> } >>> >>> +int user_creatable_help_func(void *opaque, QemuOpts *opts, Error **errp) >>> +{ >>> + Visitor *v; >>> + char *type = NULL; >>> + Error *local_err = NULL; >>> + >> >>Why this blank line? >> > I'll remove it. > >>> + int i; >>> + char *values = NULL; >>> + Object *obj; >>> + ObjectPropertyInfoList *props = NULL; >>> + ObjectProperty *prop; >>> + ObjectPropertyIterator iter; >>> + ObjectPropertyInfoList *start; >>> + >>> + struct EnumProperty { >>> + const char * const *strings; >>> + int (*get)(Object *, Error **); >>> + void (*set)(Object *, int, Error **); >>> + } *enumprop; >> >>Copied from object.c. Big no-no. Whatever you do with this probably >>belongs into object.c instead. >> > Yes, this way is ugly. > What I want to do is parsing the enum <-> string mappings through the > EnumProperty > to make the output more reasonable. > It should be parsed in object.c, But I can't assume the size of enum str > list, then can't > pre-alloc it in user_creatable_help_func. So far I havn't figured out a good > way to return > a string array that including the enum str list to user_creatable_help_func > for printing. > May I have your suggestions? See below. >>> + >>> + > v = opts_visitor_new(opts); >>> + visit_start_struct(v, NULL, NULL, 0, &local_err); >>> + if (local_err) { >>> + goto out; >>> + } >>> + >>> + visit_type_str(v, "qom-type", &type, &local_err); >>> + if (local_err) { >>> + goto out_visit; >>> + } >>> + >>> + if (type && is_help_option(type)) { >> >>I think the Options visitor is overkill. Much simpler: >> >> type = qemu_opt_get(opts, "qom-type"); >> if (type && is_help_option(type)) { >> > Yes, it is much simpler, I'll use this instead of visitor code. > >>> + printf("Available object backend types:\n"); >>> + GSList *list = object_class_get_list(TYPE_USER_CREATABLE, false); >>> + while (list) { >>> + const char *name; >>> + name = object_class_get_name(OBJECT_CLASS(list->data)); >>> + if (strcmp(name, TYPE_USER_CREATABLE)) { >> >>Why do you need to skip TYPE_USER_CREATABLE? >> >>Hmm... >> >> $ qemu-system-x86_64 -object user-creatable,id=foo >> ** >> ERROR:/work/armbru/qemu/qom/object.c:361:object_initialize_with_type: >> assertion failed (type->instance_size >= sizeof(Object)): (0 >= 40) >> Aborted (core dumped) >> >>Should this type be abstract? >> > Yes, The object type user-creatable is abstract, But obviously it missed the > abstract property. > I'll add it in next patch(patch 1/2), Then I dont need to skip it at here > anymore. Yes, please. >>> + printf("%s\n", name); >>> + } >>> + list = list->next; >>> + } >> >>Recommend to keep the loop control in one place: >> >> for (list = object_class_get_list(TYPE_USER_CREATABLE, >> false); >> list; >> list = list->next) { >> ... >> } >> >>If you hate multi-line for (...), you can do >> >> GSList *head = object_class_get_list(TYPE_USER_CREATABLE, >> false); >> >> for (list = head; list; list->next) { >> ... >> } >> > Will do it. > >>> + g_slist_free(list); >>> + goto out_visit; >>> + } >>> + >>> + if (!type || !qemu_opt_has_help_opt(opts)) { >>> + visit_end_struct(v, NULL); >>> + return 0; >>> + } >>> + >>> + if (!object_class_by_name(type)) { >>> + printf("invalid object type: %s\n", type); >>> + goto out_visit; >>> + } >>> + obj = object_new(type); >>> + object_property_iter_init(&iter, obj); >>> + >>> + while ((prop = object_property_iter_next(&iter))) { >>> + ObjectPropertyInfoList *entry = g_malloc0(sizeof(*entry)); >> >>Blank line between declarations and statements, please. >> > ok. > >>> + entry->value = g_malloc0(sizeof(ObjectPropertyInfo)); >> >>Either g_malloc0(sizeof(entry->value), or g_new0(ObjectPropertyInfo). >> > will use g_malloc0(sizeof(entry->value). > >>> + entry->next = props; >>> + props = entry; >>> + entry->value->name = g_strdup(prop->name); >>> + i = 0; >>> + enumprop = prop->opaque; >>> + if (!g_str_equal(prop->type, "string") && \ >>> + !g_str_equal(prop->type, "bool") && \ >>> + !g_str_equal(prop->type, "struct tm") && \ >>> + !g_str_equal(prop->type, "int") && \ >>> + !g_str_equal(prop->type, "uint8") && \ >>> + !g_str_equal(prop->type, "uint16") && \ >>> + !g_str_equal(prop->type, "uint32") && \ >>> + !g_str_equal(prop->type, "uint64")) { >> >>Uh, what are you trying to test with this condition? It's not obvious >>to me... >> >>> + while (enumprop->strings[i] != NULL) { >>> + if (i != 0) { >>> + values = g_strdup_printf("%s/%s", >>> + >>> values, enumprop->strings[i]); >>> + } else { >>> + values = g_strdup_printf("%s", >>> enumprop->strings[i]); >>> + } >>> + i++; >>> + } >>> + entry->value->type = g_strdup_printf("%s, available values: >>> %s", >>> + >>> prop->type, values); >> >>Looks like this is the enum case. But why is the condition true exactly >>for enums? >> > Yes, After filter all the other types above, I assume the result here is the > enum case. > I knew this way does not make sense, But I havn't figured out a proper way > yet to judge > whether the type is an enum or not. > May I have your ideas? You're messing with struct EnumProperty because you want more help than what ObjectPropertyInfo can provice. Digression on the meaning of ObjectPropertyInfo. This is its definition: ## # @ObjectPropertyInfo: # # @name: the name of the property # # @type: the type of the property. This will typically come in one of four # forms: # # 1) A primitive type such as 'u8', 'u16', 'bool', 'str', or 'double'. # These types are mapped to the appropriate JSON type. # # 2) A child type in the form 'child<subtype>' where subtype is a qdev # device type name. Child properties create the composition tree. # # 3) A link type in the form 'link<subtype>' where subtype is a qdev # device type name. Link properties form the device model graph. # # Since: 1.2 ## { 'struct': 'ObjectPropertyInfo', 'data': { 'name': 'str', 'type': 'str' } } @type is documented to be either a primitive type, a child type or a link. "Primitive type" isn't defined. The examples given suggest it's a QAPI built-in type. If that's correct, clause 1) does not cover enumeration types. However, it doesn't seem to be correct: I observe 'string', not 'str'. Paolo, Andreas, any idea what @type is supposed to mean? End of digression. All ObjectPropertyInfo gives you is two strings: a name and a type. If you need more information than that, you have to add a proper interface to get it! Say a function that takes an object and a property name, and returns information about the property's type. Probably should be two functions, one that maps QOM object and property name to the property's QOM type, one that maps a QOM type to QOM type information. In other words, you need QOM object and type introspection. Paolo, Andreas, if that already exists in some form, please point us to it. >>> + > } else { >>> + entry->value->type = g_strdup(prop->type); >>> + } >>> + } >> >>The loop above collects properties, and ... >> >>> + >>> + start = props; >>> + while (props != NULL) { >>> + ObjectPropertyInfo *value = props->value; >>> + printf("%s (%s)\n", value->name, value->type); >>> + props = props->next; >>> + } >>> + qapi_free_ObjectPropertyInfoList(start); >> >>... this loop prints them. Have you considered fusing the two loops? >> > I agreed. will merge the two loops. > >>> + >>> +out_visit: >>> + visit_end_struct(v, NULL); >>> + >>> +out: >>> + g_free(values); >>> + g_free(type); >>> + object_unref(obj); >>> + if (local_err) { >>> + error_report_err(local_err); >>> + } >>> + return 1; >>> +} >>> + >>> static void register_types(void) >>> { >>> static const TypeInfo uc_interface_info = { >>> diff --git a/vl.c b/vl.c >>> index ee557a1..a2230c8 100644 >>> --- a/vl.c >>> +++ b/vl.c >>> @@ -4251,6 +4251,11 @@ int main(int argc, char **argv, char **envp) >>> page_size_init(); >>> socket_init(); >>> >>> + if (qemu_opts_foreach(qemu_find_opts("object"), >>> user_creatable_help_func, >>> + NULL, NULL)) { >>> + exit(1); >>> + } >>> + >> >>I think this should be done as early as possible. However, we already >>do -device help nearby. Not fair to ask you to clean up this mess. >> > How about move it to here? > object_property_add_child(object_get_root(), "machine", > > OBJECT(current_machine), &error_abort); > + > + if (qemu_opts_foreach(qemu_find_opts("object"), user_creatable_help_func, > + NULL, NULL)) { > + exit(1); > + } > + > cpu_exec_init_all(); I wouldn't put it in the middle of the machine stuff. Perhaps before current_machine = MACHINE(...)? >>> if (qemu_opts_foreach(qemu_find_opts("object"), >>> >>> user_creatable_add_opts_foreach, >>> object_create_initial, >>> NULL)) { > > Thank you for reviewing the code. > Lin