docs/devel/writing-monitor-commands: Repair a decade of rot

The tutorial doesn't match reality since at least 2013.  Repairing it
involves fixing the following issues:

* Update for commit 6d32717155 (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
  e218052f92 (aio / timers: De-document -clock) back in 2014.

* Update for commit 895a2a80e0 (qapi: Use 'struct' instead of 'type'
  in schema).

* Update for commit 3313b6124b (qapi: add qapi2texi script): add
  required documentation to the schema snippets, and drop section
  "Command Documentation".

* Update for commit a3c45b3e62 (qapi: New special feature flag
  "unstable"): supply the required feature, deemphasize the x- prefix.

* Update for commit dd98234c05 (qapi: introduce x-query-roms QMP
  command): rephrase from "add new command" to "examine existing
  command".

* Update for commit 9492718b7c (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 <armbru@redhat.com>
Message-ID: <20240227115617.237875-2-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[Whitespace tidied up, typo fixed]
This commit is contained in:
Markus Armbruster 2024-02-27 12:56:16 +01:00
parent e1f684ea2e
commit 59807e2098

View file

@ -66,12 +66,13 @@ Then, in a different terminal::
"version": { "version": {
"qemu": { "qemu": {
"micro": 50, "micro": 50,
"minor": 15, "minor": 2,
"major": 0 "major": 8
}, },
"package": "" "package": ...
}, },
"capabilities": [ "capabilities": [
"oob"
] ]
} }
} }
@ -107,6 +108,11 @@ The first step is defining the command in the appropriate QAPI schema
module. We pick module qapi/misc.json, and add the following line at module. We pick module qapi/misc.json, and add the following line at
the bottom:: the bottom::
##
# @hello-world:
#
# Since: 9.0
##
{ 'command': 'hello-world' } { 'command': 'hello-world' }
The "command" keyword defines a new QMP command. It's an JSON object. All 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 Arguments
~~~~~~~~~ ~~~~~~~~~
Let's add an argument called "message" to our "hello-world" command. The new Let's add arguments to our "hello-world" command.
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.
The first change we have to do is to modify the command specification in the The first change we have to do is to modify the command specification in the
schema file to the following:: schema file to the following::
{ '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' } }
Notice the new 'data' member in the schema. It's an JSON object whose each Notice the new 'data' member in the schema. It specifies an argument
element is an argument to the command in question. Also notice the asterisk, 'message' of QAPI type 'str', and an argument 'times' of QAPI type
it's used to mark the argument optional (that means that you shouldn't use it 'int'. Also notice the asterisk, it's used to mark the argument
for mandatory arguments). Finally, 'str' is the argument's type, which optional.
stands for "string". The QAPI also supports integers, booleans, enumerations
and user defined types.
Now, let's update our C implementation in monitor/qmp-cmds.c:: Now, let's update our C implementation in monitor/qmp-cmds.c::
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) { if (!message) {
message = "Hello, world";
}
if (!has_times) {
times = 1;
}
for (int i = 0; i < times; i++) {
printf("%s\n", message); printf("%s\n", message);
} else {
printf("Hello, world\n");
} }
} }
There are two important details to be noticed: There are two important details to be noticed:
1. All optional arguments are accompanied by a 'has\_' boolean, which is set 1. Optional arguments other than pointers are accompanied by a 'has\_'
if the optional argument is present or false otherwise boolean, which is set if the optional argument is present or false
otherwise
2. The C implementation signature must follow the schema's argument ordering, 2. The C implementation signature must follow the schema's argument ordering,
which is defined by the "data" member which is defined by the "data" member
@ -254,35 +273,6 @@ If the failure you want to report falls into one of the two cases above,
use error_set() with a second argument of an ErrorClass value. use error_set() with a second argument of an ErrorClass value.
Command Documentation
~~~~~~~~~~~~~~~~~~~~~
There's only one step missing to make "hello-world"'s implementation complete,
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: <next qemu stable release, eg. 1.0>
##
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
Please, note that the "Returns" clause is optional if a command doesn't return
any data nor any errors.
Implementing the HMP command Implementing the HMP command
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -306,18 +296,20 @@ Here's the implementation of the "hello-world" HMP command::
} }
} }
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.
There are three important points to be noticed: There are four important points to be noticed:
1. The "mon" and "qdict" arguments are mandatory for all HMP functions. The 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 former is the monitor object. The latter is how the monitor passes
arguments entered by the user to the command implementation 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 hmp_handle_error() which prints a message to the user, but we could do
more, like taking different actions depending on the error more, like taking different actions depending on the error
qmp_hello_world() returns 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 QMP call
There's one last step to actually make the command available to monitor users, There's one last step to actually make the command available to monitor users,
@ -372,7 +364,7 @@ data, it is not expected that machines will need to parse the result.
The overhead of defining a fine grained QAPI type for the data may not 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 be justified by the potential benefit. In such cases, it is permitted
to have a command return a simple string that contains formatted data, 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 This indicates that the command is not guaranteed to be long term
stable / liable to change in future and is not following QAPI design stable / liable to change in future and is not following QAPI design
best practices. An example where this approach is taken is the QMP best practices. An example where this approach is taken is the QMP
@ -386,302 +378,207 @@ an illustration.
User Defined Types User Defined Types
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
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.
For this example we will write the query-alarm-clock command, which returns For each option ROM, we want to return two pieces of information: the
information about QEMU's timer alarm. For more information about it, please ROM image's file name, and its bootindex, if any. We need to create a
check the "-clock" command-line option. new QAPI type for that, as shown below::
We want to return two pieces of information. The first one is the alarm clock's
name. The second one is when the next alarm will fire. The former information 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 below::
## ##
# @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: 9.0
#
# Since: 1.0
## ##
{ 'type': 'QemuAlarmClock', { 'struct': 'OptionRomInfo',
'data': { 'clock-name': 'str', '*next-deadline': 'int' } } 'data': { 'filename': 'str', '*bootindex': 'int' } }
The "type" keyword defines a new QAPI type. Its "data" member contains the The "struct" keyword defines a new QAPI type. Its "data" member
type's members. In this example our members are the "clock-name" and the contains the type's members. In this example our members are
"next-deadline" one, which is optional. "filename" and "bootindex". The latter is optional.
Now let's define the query-alarm-clock command:: Now let's define the query-option-roms command::
## ##
# @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 # Returns: OptionRomInfo
# being currently used by QEMU (this is usually set by the '-clock'
# command-line option).
# #
# Since: 1.0 # Since: 9.0
## ##
{ 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' } { 'command': 'query-option-roms',
'returns': ['OptionRomInfo'] }
Notice the "returns" keyword. As its name suggests, it's used to define the Notice the "returns" keyword. As its name suggests, it's used to define the
data returned by a command. data returned by a command.
It's time to implement the qmp_query_alarm_clock() function, you can put it Notice the syntax ['OptionRomInfo']". This should be read as "returns
in the qemu-timer.c file:: a list of OptionRomInfo".
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; OptionRomInfoList *info_list = NULL;
int64_t deadline; OptionRomInfoList **tailp = &info_list;
OptionRomInfo *info;
clock = g_malloc0(sizeof(*clock)); for (int i = 0; i < nb_option_roms; i++) {
info = g_malloc0(sizeof(*info));
deadline = qemu_next_alarm_deadline(); info->filename = g_strdup(option_rom[i].name);
if (deadline > 0) { info->has_bootindex = option_rom[i].bootindex >= 0;
clock->has_next_deadline = true; if (info->has_bootindex) {
clock->next_deadline = deadline; info->bootindex = option_rom[i].bootindex;
}
QAPI_LIST_APPEND(tailp, info);
} }
clock->clock_name = g_strdup(alarm_timer->name);
return clock; return info_list;
} }
There are a number of things to be noticed: There are a number of things to be noticed:
1. The QemuAlarmClock type is automatically generated by the QAPI framework, 1. Type OptionRomInfo is automatically generated by the QAPI framework,
its members correspond to the type's specification in the schema file its members correspond to the type's specification in the schema
2. As specified in the schema file, the function returns a QemuAlarmClock file
instance and takes no arguments (besides the "errp" one, which is mandatory 2. Type OptionRomInfoList is also generated. It's a singly linked
for all QMP functions) list.
3. The "clock" variable (which will point to our QAPI type instance) is 3. As specified in the schema file, the function returns a
allocated by the regular g_malloc0() function. Note that we chose to OptionRomInfoList, and takes no arguments (besides the "errp" one,
initialize the memory to zero. This is recommended for all QAPI types, as which is mandatory for all QMP functions)
it helps avoiding bad surprises (specially with booleans) 4. The returned object is dynamically allocated
4. Remember that "next_deadline" is optional? Non-pointer optional 5. All strings are dynamically allocated. This is so because QAPI also
members have a 'has_TYPE_NAME' member that should be properly set 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 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 generates
a function to free its types and it cannot distinguish between dynamically
or statically allocated strings
6. You have to include "qapi/qapi-commands-misc.h" in qemu-timer.c
Time to test the new command. Build qemu, run it as described in the "Testing" Time to test the new command. Build qemu, run it as described in the "Testing"
section and try this:: section and try this::
{ "execute": "query-alarm-clock" } { "execute": "query-option-rom" }
{ {
"return": { "return": [
"next-deadline": 2368219, {
"clock-name": "dynticks" "filename": "kvmvapic.bin"
} }
]
} }
The HMP command The HMP command
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Here's the HMP counterpart of the query-alarm-clock command:: Here's the HMP counterpart of the query-option-roms command::
void hmp_info_alarm_clock(Monitor *mon) void hmp_info_option_roms(Monitor *mon, const QDict *qdict)
{ {
QemuAlarmClock *clock;
Error *err = NULL; Error *err = NULL;
OptionRomInfoList *info_list, *tail;
OptionRomInfo *info;
clock = qmp_query_alarm_clock(&err); info_list = qmp_query_option_roms(&err);
if (hmp_handle_error(mon, err)) { if (hmp_handle_error(mon, err)) {
return; return;
} }
monitor_printf(mon, "Alarm clock method in use: '%s'\n", clock->clock_name); for (tail = info_list; tail; tail = tail->next) {
if (clock->has_next_deadline) { info = tail->value;
monitor_printf(mon, "Next alarm will fire in %" PRId64 " nanoseconds\n", monitor_printf(mon, "%s", info->filename);
clock->next_deadline); if (info->has_bootindex) {
monitor_printf(mon, " %" PRId64, info->bootindex);
}
monitor_printf(mon, "\n");
} }
qapi_free_QemuAlarmClock(clock); qapi_free_OptionRomInfoList(info_list);
} }
It's important to notice that hmp_info_alarm_clock() calls It's important to notice that hmp_info_option_roms() calls
qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock(). qapi_free_OptionRomInfoList() to free the data returned by
For user defined types, the QAPI will generate a qapi_free_QAPI_TYPE_NAME() qmp_query_option_roms(). For user defined types, QAPI will generate a
function and that's what you have to use to free the types you define and qapi_free_QAPI_TYPE_NAME() function, and that's what you have to use to
qapi_free_QAPI_TYPE_NAMEList() for list types (explained in the next section). free the types you define and qapi_free_QAPI_TYPE_NAMEList() for list
If the QMP call returns a string, then you should g_free() to free it. types (explained in the next section). If the QMP function returns a
string, then you should g_free() to free it.
Also note that hmp_info_alarm_clock() performs error handling. That's not Also note that hmp_info_option_roms() performs error handling. That's
strictly required if you're sure the QMP function doesn't return errors, but not strictly required when you're sure the QMP function doesn't return
it's good practice to always check for errors. errors; you could instead pass it &error_abort then.
Another important detail is that HMP's "info" commands don't go into the Another important detail is that HMP's "info" commands go into
hmp-commands.hx. Instead, they go into the info_cmds[] table, which is defined hmp-commands-info.hx, not hmp-commands.hx. The entry for the "info
in the monitor/misc.c file. The entry for the "info alarmclock" follows:: option-roms" follows::
{ {
.name = "alarmclock", .name = "option-roms",
.args_type = "", .args_type = "",
.params = "", .params = "",
.help = "show information about the alarm clock", .help = "show roms",
.cmd = hmp_info_alarm_clock, .cmd = hmp_info_option_roms,
}, },
SRST
``info option-roms``
Show the option ROMs.
ERST
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.
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']", this
should be read as "returns a list of TimerAlarmMethod instances".
The C implementation follows::
TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp)
{
TimerAlarmMethodList *method_list = NULL;
const struct qemu_alarm_timer *p;
bool current = true;
for (p = alarm_timers; p->name; p++) {
TimerAlarmMethod *value = g_malloc0(*value);
value->method_name = g_strdup(p->name);
value->current = current;
QAPI_LIST_PREPEND(method_list, value);
current = false;
}
return method_list;
}
The most important difference from the previous examples is the
TimerAlarmMethodList type, which is automatically generated by the QAPI from
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 points 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 pointer
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 handled
by hand and we return the head of the list.
Now Build qemu, run it as explained in the "Testing" section and try our new
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 = NULL;
method_list = qmp_query_alarm_methods(&err);
if (hmp_handle_error(mon, err)) {
return;
}
for (method = method_list; method; method = 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 Writing a debugging aid returning unstructured text
--------------------------------------------------- ---------------------------------------------------
As discussed in section `Modelling data in QAPI`_, it is required that As discussed in section `Modelling data in QAPI`_, it is required that
commands expecting machine usage be using fine-grained QAPI data types. commands expecting machine usage be using fine-grained QAPI data types.
The exception to this rule applies when the command is solely intended The exception to this rule applies when the command is solely intended
as a debugging aid and allows for returning unstructured text. This is as a debugging aid and allows for returning unstructured text, such as
commonly needed for query commands that report aspects of QEMU's a query command that report aspects of QEMU's internal state that are
internal state that are useful to human operators. useful only to human operators.
In this example we will consider a simplified variant of the HMP In this example we will consider the existing QMP command
command ``info roms``. Following the earlier rules, this command will ``x-query-roms`` in qapi/machine.json. It has no parameters and
need to live under the ``x-`` name prefix, so its QMP implementation returns a ``HumanReadableText``::
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' } }
##
# @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', { 'command': 'x-query-roms',
'returns': 'HumanReadableText' } 'returns': 'HumanReadableText',
'features': [ 'unstable' ] }
The ``HumanReadableText`` struct is intended to be used for all The ``HumanReadableText`` struct is defined in qapi/common.json as a
commands, under the ``x-`` name prefix that are returning unstructured struct with a string member. It is intended to be used for all
text targeted at humans. It should never be used for commands outside commands that are returning unstructured text targeted at
the ``x-`` name prefix, as those should be using structured QAPI types. 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.
Implementing the QMP command Implementing the QMP command
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The QMP implementation will typically involve creating a ``GString`` 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::
HumanReadableText *qmp_x_query_roms(Error **errp) 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); return human_readable_text_from_str(buf);
} }
The actual implementation emits more information. You can find it in
hw/core/loader.c.
Implementing the HMP command 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 the human monitor (HMP) as shown in previous examples. The HMP
implementations will all look fairly similar, as all they need do is implementations will all look fairly similar, as all they need do is
invoke the QMP command and then print the resulting text or error 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::
void hmp_info_roms(Monitor *mon, const QDict *qdict) void hmp_info_roms(Monitor *mon, const QDict *qdict)
{ {
@ -746,3 +646,5 @@ field NULL::
.help = "show roms", .help = "show roms",
.cmd_info_hrt = qmp_x_query_roms, .cmd_info_hrt = qmp_x_query_roms,
}, },
This is how the actual HMP command is done.