From nobody Tue Nov 26 08:31:14 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1709035063; cv=none; d=zohomail.com; s=zohoarc; b=e1bzAO3/lCvycjihUORA9qiGYSWDs0aPqSVqiiK0P460692amWy8g8ld6Dxq/pgxJgD+Sru+Bp0rn24jq5ql2XmGqn6KgcYcGGsbhEPQAFAkK3Y/FvGfQ+weLL2JYx5Rnf9qfDe/sxdvyu86fXzhM4V5e/Ch8iinnmZ/AgS0e7w= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1709035063; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=e2N8g16fUZqRt5+xqIV0kcapDtska+RnFLeScqC9qzM=; b=F+cdPv0C/gl+FWTtKvCLtg/6cAIZza363GflglM7cWSrGWmfHKZV+5H6lNeJ9KfJn+4P/1osDzsS0mpPhnslGgr/iEc2RwOG8gKR3ISCfYAL9OPZSEnmV4/wmhb+QwZrKD72asyWQljf9Gm3YHMpvuGf3VKRbMIw52kskFv2oRg= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1709035063802564.5049425370995; Tue, 27 Feb 2024 03:57:43 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rew4R-0005Fl-1S; Tue, 27 Feb 2024 06:56:27 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rew4O-0005F7-U0 for qemu-devel@nongnu.org; Tue, 27 Feb 2024 06:56:24 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rew4L-0000MT-5M for qemu-devel@nongnu.org; Tue, 27 Feb 2024 06:56:24 -0500 Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-145-OzLSQ9-mMh-cXVVhndHyqA-1; Tue, 27 Feb 2024 06:56:18 -0500 Received: from smtp.corp.redhat.com (int-mx09.intmail.prod.int.rdu2.redhat.com [10.11.54.9]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 6F6E21C05AAC for ; Tue, 27 Feb 2024 11:56:18 +0000 (UTC) Received: from blackfin.pond.sub.org (unknown [10.39.193.4]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 12591492BC8 for ; Tue, 27 Feb 2024 11:56:18 +0000 (UTC) Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id 1ADB021E66FB; Tue, 27 Feb 2024 12:56:17 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1709034980; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=e2N8g16fUZqRt5+xqIV0kcapDtska+RnFLeScqC9qzM=; b=GXk95a4BCrFJ4v9/IpJUTN2+PnHcM/OBtQUcxMq1fq44673aj/uNF86boOJPGZKM1u9sN3 sie+5OljEEZd0WNVyjE0zxvru3Rh+5Mn7FkzEkT/HJ7/hxbQHQ9oMCSmT3Eo3KZZcO8vN4 LREgV+e7K7JPp0PhD+TsWT+7Sv7vaFc= X-MC-Unique: OzLSQ9-mMh-cXVVhndHyqA-1 From: Markus Armbruster To: qemu-devel@nongnu.org Cc: eblake@redhat.com, berrange@redhat.com Subject: [PATCH 1/2] docs/devel/writing-monitor-commands: Repair a decade of rot Date: Tue, 27 Feb 2024 12:56:16 +0100 Message-ID: <20240227115617.237875-2-armbru@redhat.com> In-Reply-To: <20240227115617.237875-1-armbru@redhat.com> References: <20240227115617.237875-1-armbru@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.9 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.129.124; envelope-from=armbru@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.088, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1709035065222100003 Content-Type: text/plain; charset="utf-8" The tutorial doesn't match reality since at least 2013. Repairing it involves fixing the following issues: * Update for commit 6d327171551 (aio / timers: Remove alarm timers): replace the broken examples. Instead of having one for returning a struct and another for returning a list of structs, do just one for the latter. This resolves the FIXME added in commit e218052f928 (aio / timers: De-document -clock) back in 2014. * Update for commit 895a2a80e0e (qapi: Use 'struct' instead of 'type' in schema). * Update for commit 3313b6124b5 (qapi: add qapi2texi script): add required documentation to the schema snippets, and drop section "Command Documentation". * Update for commit a3c45b3e629 (qapi: New special feature flag "unstable"): supply the required feature, deemphasize the x- prefix. * Update for commit dd98234c059 (qapi: introduce x-query-roms QMP command): rephrase from "add new command" to "examine existing command". * Update for commit 9492718b7c0 (qapi misc: Elide redundant has_FOO in generated C): hello-world's message argument no longer comes with a has_message, add a second argument that does. * Update for moved and renamed files. While there, update QMP version output to current output. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake --- docs/devel/writing-monitor-commands.rst | 460 ++++++++++-------------- 1 file changed, 181 insertions(+), 279 deletions(-) diff --git a/docs/devel/writing-monitor-commands.rst b/docs/devel/writing-m= onitor-commands.rst index b6ee4fa063..047c6f42bb 100644 --- a/docs/devel/writing-monitor-commands.rst +++ b/docs/devel/writing-monitor-commands.rst @@ -66,12 +66,13 @@ Then, in a different terminal:: "version": { "qemu": { "micro": 50, - "minor": 15, - "major": 0 + "minor": 2, + "major": 8 }, - "package": "" + "package": ... }, "capabilities": [ + "oob" ] } } @@ -107,6 +108,11 @@ The first step is defining the command in the appropri= ate QAPI schema module. We pick module qapi/misc.json, and add the following line at the bottom:: =20 + ## + # @hello-world: + # + # Since: 9.0 + ## { 'command': 'hello-world' } =20 The "command" keyword defines a new QMP command. It's an JSON object. All @@ -148,37 +154,50 @@ you don't see it then something went wrong. Arguments ~~~~~~~~~ =20 -Let's add an argument called "message" to our "hello-world" command. The n= ew -argument will contain the string to be printed to stdout. It's an optional -argument, if it's not present we print our default "Hello, World" string. +Let's add arguments to our "hello-world" command. =20 The first change we have to do is to modify the command specification in t= he schema file to the following:: =20 - { 'command': 'hello-world', 'data': { '*message': 'str' } } + ## + # @hello-world: + # + # @message: message to be printed (default: "Hello, world!") + # + # @times: how many times to print the message (default: 1) + # + # Since: 9.0 + ## + { 'command': 'hello-world', + 'data': { '*message': 'str', '*times': 'int' } } =20 -Notice the new 'data' member in the schema. It's an JSON object whose each -element is an argument to the command in question. Also notice the asteris= k, -it's used to mark the argument optional (that means that you shouldn't use= it -for mandatory arguments). Finally, 'str' is the argument's type, which -stands for "string". The QAPI also supports integers, booleans, enumeratio= ns -and user defined types. +Notice the new 'data' member in the schema. It specifies an argument +'message' of QAPI type 'str', and an argument 'times' of QAPI type +'int'. Also notice the asterisk, it's used to mark the argument +optional. =20 Now, let's update our C implementation in monitor/qmp-cmds.c:: =20 - void qmp_hello_world(const char *message, Error **errp) + void qmp_hello_world(const char *message, bool has_times, int64_t times, + Error **errp) { - if (message) { - printf("%s\n", message); - } else { - printf("Hello, world\n"); + if (!message) { + message =3D "Hello, world"; + } + if (!has_times) { + times =3D 1; + } + + for (int i =3D 0; i < times; i++) { + printf("%s\n", message); } } =20 There are two important details to be noticed: =20 -1. All optional arguments are accompanied by a 'has\_' boolean, which is s= et - if the optional argument is present or false otherwise +1. Optional arguments other than pointers are accompanied by a 'has\_' + boolean, which is set if the optional argument is present or false + otherwise 2. The C implementation signature must follow the schema's argument orderi= ng, which is defined by the "data" member =20 @@ -254,35 +273,6 @@ If the failure you want to report falls into one of th= e two cases above, use error_set() with a second argument of an ErrorClass value. =20 =20 -Command Documentation -~~~~~~~~~~~~~~~~~~~~~ - -There's only one step missing to make "hello-world"'s implementation compl= ete, -and that's its documentation in the schema file. - -There are many examples of such documentation in the schema file already, = but -here goes "hello-world"'s new entry for qapi/misc.json:: - - ## - # @hello-world: - # - # Print a client provided string to the standard output stream. - # - # @message: string to be printed - # - # Returns: Nothing on success. - # - # Notes: if @message is not provided, the "Hello, world" string will - # be printed instead - # - # Since: - ## - { 'command': 'hello-world', 'data': { '*message': 'str' } } - -Please, note that the "Returns" clause is optional if a command doesn't re= turn -any data nor any errors. - - Implementing the HMP command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =20 @@ -306,18 +296,20 @@ Here's the implementation of the "hello-world" HMP co= mmand:: } } =20 -Also, you have to add the function's prototype to the hmp.h file. +Add it to monitor/hmp-cmds.c. Also, add its prototype to +include/monitor/hmp.h. =20 -There are three important points to be noticed: +There are four important points to be noticed: =20 1. The "mon" and "qdict" arguments are mandatory for all HMP functions. The former is the monitor object. The latter is how the monitor passes arguments entered by the user to the command implementation -2. hmp_hello_world() performs error checking. In this example we just call +2. We chose not to support the "times" argument in HMP +3. hmp_hello_world() performs error checking. In this example we just call hmp_handle_error() which prints a message to the user, but we could do more, like taking different actions depending on the error qmp_hello_world() returns -3. The "err" variable must be initialized to NULL before performing the +4. The "err" variable must be initialized to NULL before performing the QMP call =20 There's one last step to actually make the command available to monitor us= ers, @@ -372,7 +364,7 @@ data, it is not expected that machines will need to par= se the result. The overhead of defining a fine grained QAPI type for the data may not be justified by the potential benefit. In such cases, it is permitted to have a command return a simple string that contains formatted data, -however, it is mandatory for the command to use the 'x-' name prefix. +however, it is mandatory for the command to be marked unstable. This indicates that the command is not guaranteed to be long term stable / liable to change in future and is not following QAPI design best practices. An example where this approach is taken is the QMP @@ -386,302 +378,207 @@ an illustration. User Defined Types ~~~~~~~~~~~~~~~~~~ =20 -FIXME This example needs to be redone after commit 6d32717 +For this example we will write the query-option-roms command, which +returns information about ROMs loaded into the option ROM space. For +more information about it, please check the "-option-rom" command-line +option. =20 -For this example we will write the query-alarm-clock command, which returns -information about QEMU's timer alarm. For more information about it, please -check the "-clock" command-line option. - -We want to return two pieces of information. The first one is the alarm cl= ock's -name. The second one is when the next alarm will fire. The former informat= ion is -returned as a string, the latter is an integer in nanoseconds (which is not -very useful in practice, as the timer has probably already fired when the -information reaches the client). - -The best way to return that data is to create a new QAPI type, as shown be= low:: +For each option ROM, we want to return two pieces of information: the +ROM image's file name, and its bootindex, if any. We need to create a +new QAPI type for that, as shown below:: =20 ## - # @QemuAlarmClock + # @OptionRomInfo: # - # QEMU alarm clock information. + # @filename: option ROM image file name # - # @clock-name: The alarm clock method's name. + # @bootindex: option ROM's bootindex # - # @next-deadline: The time (in nanoseconds) the next alarm will fire. - # - # Since: 1.0 + # Since: 9.0 ## - { 'type': 'QemuAlarmClock', - 'data': { 'clock-name': 'str', '*next-deadline': 'int' } } + { 'struct': 'OptionRomInfo', + 'data': { 'filename': 'str', '*bootindex': 'int' } } =20 -The "type" keyword defines a new QAPI type. Its "data" member contains the -type's members. In this example our members are the "clock-name" and the -"next-deadline" one, which is optional. +The "struct" keyword defines a new QAPI type. Its "data" member +contains the type's members. In this example our members are +"filename" and "bootindex". The latter is optional. =20 -Now let's define the query-alarm-clock command:: +Now let's define the query-option-roms command:: =20 ## - # @query-alarm-clock + # @query-option-roms: # - # Return information about QEMU's alarm clock. + # Query information on ROMs loaded into the option ROM space. # - # Returns a @QemuAlarmClock instance describing the alarm clock method - # being currently used by QEMU (this is usually set by the '-clock' - # command-line option). + # Returns: OptionRomInfo # - # Since: 1.0 + # Since: 9.0 ## - { 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' } + { 'command': 'query-option-roms', + 'returns': ['OptionRomInfo'] } =20 Notice the "returns" keyword. As its name suggests, it's used to define the data returned by a command. =20 -It's time to implement the qmp_query_alarm_clock() function, you can put it -in the qemu-timer.c file:: +Notice the syntax ['OptionRomInfo']". This should be read as "returns +a list of OptionRomInfo". =20 - QemuAlarmClock *qmp_query_alarm_clock(Error **errp) +It's time to implement the qmp_query_option_roms() function. Add to +monitor/qmp-cmds.c:: + + OptionRomInfoList *qmp_query_option_roms(Error **errp) { - QemuAlarmClock *clock; - int64_t deadline; + OptionRomInfoList *info_list =3D NULL; + OptionRomInfoList **tailp =3D &info_list; + OptionRomInfo *info; =20 - clock =3D g_malloc0(sizeof(*clock)); - - deadline =3D qemu_next_alarm_deadline(); - if (deadline > 0) { - clock->has_next_deadline =3D true; - clock->next_deadline =3D deadline; + for (int i =3D 0; i < nb_option_roms; i++) { + info =3D g_malloc0(sizeof(*info)); + info->filename =3D g_strdup(option_rom[i].name); + info->has_bootindex =3D option_rom[i].bootindex >=3D 0; + if (info->has_bootindex) { + info->bootindex =3D option_rom[i].bootindex; + } + QAPI_LIST_APPEND(tailp, info); } - clock->clock_name =3D g_strdup(alarm_timer->name); =20 - return clock; + return info_list; } =20 There are a number of things to be noticed: =20 -1. The QemuAlarmClock type is automatically generated by the QAPI framewor= k, - its members correspond to the type's specification in the schema file -2. As specified in the schema file, the function returns a QemuAlarmClock - instance and takes no arguments (besides the "errp" one, which is manda= tory - for all QMP functions) -3. The "clock" variable (which will point to our QAPI type instance) is - allocated by the regular g_malloc0() function. Note that we chose to - initialize the memory to zero. This is recommended for all QAPI types, = as - it helps avoiding bad surprises (specially with booleans) -4. Remember that "next_deadline" is optional? Non-pointer optional - members have a 'has_TYPE_NAME' member that should be properly set +1. Type OptionRomInfo is automatically generated by the QAPI framework, + its members correspond to the type's specification in the schema + file +2. Type OptionRomInfoList is also generated. It's a singly linked + list. +3. As specified in the schema file, the function returns a + OptionRomInfoList, and takes no arguments (besides the "errp" one, + which is mandatory for all QMP functions) +4. The returned object is dynamically allocated +5. All strings are dynamically allocated. This is so because QAPI also + generates a function to free its types and it cannot distinguish + between dynamically or statically allocated strings +6. Remember that "bootindex" is optional? As a non-pointer optional + member, it comes with a 'has_bootindex' member that needs to be set by the implementation, as shown above -5. Even static strings, such as "alarm_timer->name", should be dynamically - allocated by the implementation. This is so because the QAPI also gener= ates - a function to free its types and it cannot distinguish between dynamica= lly - or statically allocated strings -6. You have to include "qapi/qapi-commands-misc.h" in qemu-timer.c =20 Time to test the new command. Build qemu, run it as described in the "Test= ing" section and try this:: =20 - { "execute": "query-alarm-clock" } + { "execute": "query-option-rom" } { - "return": { - "next-deadline": 2368219, - "clock-name": "dynticks" - } + "return": [ + { + "filename": "kvmvapic.bin" + } + ] } =20 =20 The HMP command ~~~~~~~~~~~~~~~ =20 -Here's the HMP counterpart of the query-alarm-clock command:: +Here's the HMP counterpart of the query-option-roms command:: =20 - void hmp_info_alarm_clock(Monitor *mon) + void hmp_info_option_roms(Monitor *mon, const QDict *qdict) { - QemuAlarmClock *clock; Error *err =3D NULL; + OptionRomInfoList *info_list, *tail; + OptionRomInfo *info; =20 - clock =3D qmp_query_alarm_clock(&err); + info_list =3D qmp_query_option_roms(&err); if (hmp_handle_error(mon, err)) { - return; + return; } =20 - monitor_printf(mon, "Alarm clock method in use: '%s'\n", clock->clock= _name); - if (clock->has_next_deadline) { - monitor_printf(mon, "Next alarm will fire in %" PRId64 " nanoseco= nds\n", - clock->next_deadline); + for (tail =3D info_list; tail; tail =3D tail->next) { + info =3D tail->value; + monitor_printf(mon, "%s", info->filename); + if (info->has_bootindex) { + monitor_printf(mon, " %" PRId64, info->bootindex); + } + monitor_printf(mon, "\n"); } =20 - qapi_free_QemuAlarmClock(clock); + qapi_free_OptionRomInfoList(info_list); } =20 -It's important to notice that hmp_info_alarm_clock() calls -qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_cl= ock(). -For user defined types, the QAPI will generate a qapi_free_QAPI_TYPE_NAME() -function and that's what you have to use to free the types you define and -qapi_free_QAPI_TYPE_NAMEList() for list types (explained in the next secti= on). -If the QMP call returns a string, then you should g_free() to free it. +It's important to notice that hmp_info_option_roms() calls +qapi_free_OptionRomInfoList() to free the data returned by +qmp_query_option_roms(). For user defined types, QAPI will generate a +qapi_free_QAPI_TYPE_NAME() function, and that's what you have to use to +free the types you define and qapi_free_QAPI_TYPE_NAMEList() for list +types (explained in the next section). If the QMP function returns a +string, then you should g_free() to free it. =20 -Also note that hmp_info_alarm_clock() performs error handling. That's not -strictly required if you're sure the QMP function doesn't return errors, b= ut -it's good practice to always check for errors. +Also note that hmp_info_option_roms() performs error handling. That's +not strictly required when you're sure the QMP function doesn't return +errors; you could instead pass it &error_abort then. =20 -Another important detail is that HMP's "info" commands don't go into the -hmp-commands.hx. Instead, they go into the info_cmds[] table, which is def= ined -in the monitor/misc.c file. The entry for the "info alarmclock" follows:: +Another important detail is that HMP's "info" commands go into +hmp-commands-info.hx, not hmp-commands.hx. The entry for the "info +option-roms" follows:: =20 - { - .name =3D "alarmclock", - .args_type =3D "", - .params =3D "", - .help =3D "show information about the alarm clock", - .cmd =3D hmp_info_alarm_clock, - }, + { + .name =3D "option-roms", + .args_type =3D "", + .params =3D "", + .help =3D "show roms", + .cmd =3D hmp_info_option_roms, + }, + SRST + ``info option-roms`` + Show the option ROMs. + ERST =20 -To test this, run qemu and type "info alarmclock" in the user monitor. +To test this, run qemu and type "info option-roms" in the user monitor. =20 =20 -Returning Lists -~~~~~~~~~~~~~~~ - -For this example, we're going to return all available methods for the timer -alarm, which is pretty much what the command-line option "-clock ?" does, -except that we're also going to inform which method is in use. - -This first step is to define a new type:: - - ## - # @TimerAlarmMethod - # - # Timer alarm method information. - # - # @method-name: The method's name. - # - # @current: true if this alarm method is currently in use, false otherwise - # - # Since: 1.0 - ## - { 'type': 'TimerAlarmMethod', - 'data': { 'method-name': 'str', 'current': 'bool' } } - -The command will be called "query-alarm-methods", here is its schema -specification:: - - ## - # @query-alarm-methods - # - # Returns information about available alarm methods. - # - # Returns: a list of @TimerAlarmMethod for each method - # - # Since: 1.0 - ## - { 'command': 'query-alarm-methods', 'returns': ['TimerAlarmMethod'] } - -Notice the syntax for returning lists "'returns': ['TimerAlarmMethod']", t= his -should be read as "returns a list of TimerAlarmMethod instances". - -The C implementation follows:: - - TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp) - { - TimerAlarmMethodList *method_list =3D NULL; - const struct qemu_alarm_timer *p; - bool current =3D true; - - for (p =3D alarm_timers; p->name; p++) { - TimerAlarmMethod *value =3D g_malloc0(*value); - value->method_name =3D g_strdup(p->name); - value->current =3D current; - QAPI_LIST_PREPEND(method_list, value); - current =3D false; - } - - return method_list; - } - -The most important difference from the previous examples is the -TimerAlarmMethodList type, which is automatically generated by the QAPI fr= om -the TimerAlarmMethod type. - -Each list node is represented by a TimerAlarmMethodList instance. We have = to -allocate it, and that's done inside the for loop: the "info" pointer point= s to -an allocated node. We also have to allocate the node's contents, which is -stored in its "value" member. In our example, the "value" member is a poin= ter -to an TimerAlarmMethod instance. - -Notice that the "current" variable is used as "true" only in the first -iteration of the loop. That's because the alarm timer method in use is the -first element of the alarm_timers array. Also notice that QAPI lists are h= andled -by hand and we return the head of the list. - -Now Build qemu, run it as explained in the "Testing" section and try our n= ew -command:: - - { "execute": "query-alarm-methods" } - { - "return": [ - { - "current": false, - "method-name": "unix" - }, - { - "current": true, - "method-name": "dynticks" - } - ] - } - -The HMP counterpart is a bit more complex than previous examples because it -has to traverse the list, it's shown below for reference:: - - void hmp_info_alarm_methods(Monitor *mon) - { - TimerAlarmMethodList *method_list, *method; - Error *err =3D NULL; - - method_list =3D qmp_query_alarm_methods(&err); - if (hmp_handle_error(mon, err)) { - return; - } - - for (method =3D method_list; method; method =3D method->next) { - monitor_printf(mon, "%c %s\n", method->value->current ? '*' : ' ', - method->value->method_name); - } - - qapi_free_TimerAlarmMethodList(method_list); - } - Writing a debugging aid returning unstructured text --------------------------------------------------- =20 As discussed in section `Modelling data in QAPI`_, it is required that commands expecting machine usage be using fine-grained QAPI data types. The exception to this rule applies when the command is solely intended -as a debugging aid and allows for returning unstructured text. This is -commonly needed for query commands that report aspects of QEMU's -internal state that are useful to human operators. +as a debugging aid and allows for returning unstructured text, such as +a query command that report aspects of QEMU's internal state that are +useful only to human operators. =20 -In this example we will consider a simplified variant of the HMP -command ``info roms``. Following the earlier rules, this command will -need to live under the ``x-`` name prefix, so its QMP implementation -will be called ``x-query-roms``. It will have no parameters and will -return a single text string:: - - { 'struct': 'HumanReadableText', - 'data': { 'human-readable-text': 'str' } } +In this example we will consider the existing QMP command +``x-query-roms`` in qapi/machine.json. It has no parameters and +returns a ``HumanReadableText``:: =20 + ## + # @x-query-roms: + # + # Query information on the registered ROMS + # + # Features: + # + # @unstable: This command is meant for debugging. + # + # Returns: registered ROMs + # + # Since: 6.2 + ## { 'command': 'x-query-roms', - 'returns': 'HumanReadableText' } + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } =20 -The ``HumanReadableText`` struct is intended to be used for all -commands, under the ``x-`` name prefix that are returning unstructured -text targeted at humans. It should never be used for commands outside -the ``x-`` name prefix, as those should be using structured QAPI types. +The ``HumanReadableText`` struct is defined in qapi/common.json as a +struct with a string member. It is intended to be used for all +commands that are returning unstructured text targeted at +humans. These should all have feature 'unstable'. Note that the +feature's documentation states why the command is unstable. WE +commonly use a ``x-`` command name prefix to make lack of stability +obvious to human users. =20 Implementing the QMP command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ =20 The QMP implementation will typically involve creating a ``GString`` -object and printing formatted data into it:: +object and printing formatted data into it, like this:: =20 HumanReadableText *qmp_x_query_roms(Error **errp) { @@ -698,6 +595,9 @@ object and printing formatted data into it:: return human_readable_text_from_str(buf); } =20 +The actual implementation emits more information. You can find it in +hw/core/loader.c. + =20 Implementing the HMP command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -706,7 +606,7 @@ Now that the QMP command is in place, we can also make = it available in the human monitor (HMP) as shown in previous examples. The HMP implementations will all look fairly similar, as all they need do is invoke the QMP command and then print the resulting text or error -message. Here's the implementation of the "info roms" HMP command:: +message. Here's an implementation of the "info roms" HMP command:: =20 void hmp_info_roms(Monitor *mon, const QDict *qdict) { @@ -746,3 +646,5 @@ field NULL:: .help =3D "show roms", .cmd_info_hrt =3D qmp_x_query_roms, }, + +This is how the actual HMP command is done. --=20 2.43.0