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
1 changed files with 184 additions and 282 deletions

View File

@ -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 appropriate QAPI schema
module. We pick module qapi/misc.json, and add the following line at
the bottom::
##
# @hello-world:
#
# Since: 9.0
##
{ 'command': 'hello-world' }
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
~~~~~~~~~
Let's add an argument called "message" to our "hello-world" command. The new
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.
The first change we have to do is to modify the command specification in the
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
element is an argument to the command in question. Also notice the asterisk,
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, enumerations
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.
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);
} else {
printf("Hello, world\n");
}
}
There are two important details to be noticed:
1. All optional arguments are accompanied by a 'has\_' boolean, which is set
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 ordering,
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.
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -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
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
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
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
~~~~~~~~~~~~~~~~~~
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
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 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::
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::
##
# @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' } }
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.
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
# 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'] }
Notice the "returns" keyword. As its name suggests, it's used to define the
data returned by a command.
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".
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 = NULL;
OptionRomInfoList **tailp = &info_list;
OptionRomInfo *info;
clock = g_malloc0(sizeof(*clock));
deadline = qemu_next_alarm_deadline();
if (deadline > 0) {
clock->has_next_deadline = true;
clock->next_deadline = deadline;
for (int i = 0; i < nb_option_roms; i++) {
info = g_malloc0(sizeof(*info));
info->filename = g_strdup(option_rom[i].name);
info->has_bootindex = option_rom[i].bootindex >= 0;
if (info->has_bootindex) {
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:
1. The QemuAlarmClock type is automatically generated by the QAPI framework,
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 mandatory
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 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"
section and try this::
{ "execute": "query-alarm-clock" }
{ "execute": "query-option-rom" }
{
"return": {
"next-deadline": 2368219,
"clock-name": "dynticks"
}
"return": [
{
"filename": "kvmvapic.bin"
}
]
}
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;
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)) {
return;
}
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 " nanoseconds\n",
clock->next_deadline);
}
qapi_free_QemuAlarmClock(clock);
}
It's important to notice that hmp_info_alarm_clock() calls
qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock().
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 section).
If the QMP call returns a string, then you should g_free() to free it.
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, but
it's good practice to always check for errors.
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 defined
in the monitor/misc.c file. The entry for the "info alarmclock" follows::
{
.name = "alarmclock",
.args_type = "",
.params = "",
.help = "show information about the alarm clock",
.cmd = hmp_info_alarm_clock,
},
To test this, run qemu and type "info alarmclock" 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"
for (tail = info_list; tail; tail = tail->next) {
info = tail->value;
monitor_printf(mon, "%s", info->filename);
if (info->has_bootindex) {
monitor_printf(mon, " %" PRId64, info->bootindex);
}
]
}
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;
monitor_printf(mon, "\n");
}
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);
qapi_free_OptionRomInfoList(info_list);
}
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.
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.
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::
{
.name = "option-roms",
.args_type = "",
.params = "",
.help = "show roms",
.cmd = hmp_info_option_roms,
},
SRST
``info option-roms``
Show the option ROMs.
ERST
To test this, run qemu and type "info option-roms" in the user monitor.
Writing a debugging aid returning unstructured text
---------------------------------------------------
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.
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``::
##
# @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' ] }
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.
Implementing the QMP command
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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)
{
@ -698,6 +595,9 @@ object and printing formatted data into it::
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -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::
void hmp_info_roms(Monitor *mon, const QDict *qdict)
{
@ -746,3 +646,5 @@ field NULL::
.help = "show roms",
.cmd_info_hrt = qmp_x_query_roms,
},
This is how the actual HMP command is done.