#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/qemu-print.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-machine.h"
+#include "qapi/type-helpers.h"
#include "hw/core/tcg-cpu-ops.h"
#include "trace.h"
#include "disas/disas.h"
#include "exec/cpu-all.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/replay.h"
+#include "sysemu/tcg.h"
#include "exec/helper-proto.h"
#include "tb-hash.h"
#include "tb-context.h"
#ifndef CONFIG_USER_ONLY
-void dump_drift_info(void)
+void dump_drift_info(GString *buf)
{
if (!icount_enabled()) {
return;
}
- qemu_printf("Host - Guest clock %"PRIi64" ms\n",
- (cpu_get_clock() - icount_get()) / SCALE_MS);
+ g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n",
+ (cpu_get_clock() - icount_get()) / SCALE_MS);
if (icount_align_option) {
- qemu_printf("Max guest delay %"PRIi64" ms\n",
- -max_delay / SCALE_MS);
- qemu_printf("Max guest advance %"PRIi64" ms\n",
- max_advance / SCALE_MS);
+ g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n",
+ -max_delay / SCALE_MS);
+ g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n",
+ max_advance / SCALE_MS);
} else {
- qemu_printf("Max guest delay NA\n");
- qemu_printf("Max guest advance NA\n");
+ g_string_append_printf(buf, "Max guest delay NA\n");
+ g_string_append_printf(buf, "Max guest advance NA\n");
}
}
+HumanReadableText *qmp_x_query_jit(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+
+ if (!tcg_enabled()) {
+ error_setg(errp, "JIT information is only available with accel=tcg");
+ return NULL;
+ }
+
+ dump_exec_info(buf);
+ dump_drift_info(buf);
+
+ return human_readable_text_from_str(buf);
+}
+
+HumanReadableText *qmp_x_query_opcount(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+
+ if (!tcg_enabled()) {
+ error_setg(errp, "Opcode count information is only available with accel=tcg");
+ return NULL;
+ }
+
+ dump_opcount_info(buf);
+
+ return human_readable_text_from_str(buf);
+}
+
#endif /* !CONFIG_USER_ONLY */
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-machine.h"
#include "exec/exec-all.h"
#include "monitor/monitor.h"
#include "sysemu/tcg.h"
-static void hmp_info_jit(Monitor *mon, const QDict *qdict)
-{
- if (!tcg_enabled()) {
- error_report("JIT information is only available with accel=tcg");
- return;
- }
-
- dump_exec_info();
- dump_drift_info();
-}
-
-static void hmp_info_opcount(Monitor *mon, const QDict *qdict)
-{
- dump_opcount_info();
-}
-
static void hmp_tcg_register(void)
{
- monitor_register_hmp("jit", true, hmp_info_jit);
- monitor_register_hmp("opcount", true, hmp_info_opcount);
+ monitor_register_hmp_info_hrt("jit", qmp_x_query_jit);
+ monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount);
}
type_init(hmp_tcg_register);
cpu_loop_exit_noexc(cpu);
}
-static void print_qht_statistics(struct qht_stats hst)
+static void print_qht_statistics(struct qht_stats hst, GString *buf)
{
uint32_t hgram_opts;
size_t hgram_bins;
if (!hst.head_buckets) {
return;
}
- qemu_printf("TB hash buckets %zu/%zu (%0.2f%% head buckets used)\n",
- hst.used_head_buckets, hst.head_buckets,
- (double)hst.used_head_buckets / hst.head_buckets * 100);
+ g_string_append_printf(buf, "TB hash buckets %zu/%zu "
+ "(%0.2f%% head buckets used)\n",
+ hst.used_head_buckets, hst.head_buckets,
+ (double)hst.used_head_buckets /
+ hst.head_buckets * 100);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT;
hgram_opts |= QDIST_PR_NODECIMAL;
}
hgram = qdist_pr(&hst.occupancy, 10, hgram_opts);
- qemu_printf("TB hash occupancy %0.2f%% avg chain occ. Histogram: %s\n",
- qdist_avg(&hst.occupancy) * 100, hgram);
+ g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. "
+ "Histogram: %s\n",
+ qdist_avg(&hst.occupancy) * 100, hgram);
g_free(hgram);
hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS;
hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE;
}
hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts);
- qemu_printf("TB hash avg chain %0.3f buckets. Histogram: %s\n",
- qdist_avg(&hst.chain), hgram);
+ g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. "
+ "Histogram: %s\n",
+ qdist_avg(&hst.chain), hgram);
g_free(hgram);
}
return false;
}
-void dump_exec_info(void)
+void dump_exec_info(GString *buf)
{
struct tb_tree_stats tst = {};
struct qht_stats hst;
tcg_tb_foreach(tb_tree_stats_iter, &tst);
nb_tbs = tst.nb_tbs;
/* XXX: avoid using doubles ? */
- qemu_printf("Translation buffer state:\n");
+ g_string_append_printf(buf, "Translation buffer state:\n");
/*
* Report total code size including the padding and TB structs;
* otherwise users might think "-accel tcg,tb-size" is not honoured.
* For avg host size we use the precise numbers from tb_tree_stats though.
*/
- qemu_printf("gen code size %zu/%zu\n",
- tcg_code_size(), tcg_code_capacity());
- qemu_printf("TB count %zu\n", nb_tbs);
- qemu_printf("TB avg target size %zu max=%zu bytes\n",
- nb_tbs ? tst.target_size / nb_tbs : 0,
- tst.max_target_size);
- qemu_printf("TB avg host size %zu bytes (expansion ratio: %0.1f)\n",
- nb_tbs ? tst.host_size / nb_tbs : 0,
- tst.target_size ? (double)tst.host_size / tst.target_size : 0);
- qemu_printf("cross page TB count %zu (%zu%%)\n", tst.cross_page,
- nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
- qemu_printf("direct jump count %zu (%zu%%) (2 jumps=%zu %zu%%)\n",
- tst.direct_jmp_count,
- nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
- tst.direct_jmp2_count,
- nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
+ g_string_append_printf(buf, "gen code size %zu/%zu\n",
+ tcg_code_size(), tcg_code_capacity());
+ g_string_append_printf(buf, "TB count %zu\n", nb_tbs);
+ g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n",
+ nb_tbs ? tst.target_size / nb_tbs : 0,
+ tst.max_target_size);
+ g_string_append_printf(buf, "TB avg host size %zu bytes "
+ "(expansion ratio: %0.1f)\n",
+ nb_tbs ? tst.host_size / nb_tbs : 0,
+ tst.target_size ?
+ (double)tst.host_size / tst.target_size : 0);
+ g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n",
+ tst.cross_page,
+ nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0);
+ g_string_append_printf(buf, "direct jump count %zu (%zu%%) "
+ "(2 jumps=%zu %zu%%)\n",
+ tst.direct_jmp_count,
+ nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0,
+ tst.direct_jmp2_count,
+ nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0);
qht_statistics_init(&tb_ctx.htable, &hst);
- print_qht_statistics(hst);
+ print_qht_statistics(hst, buf);
qht_statistics_destroy(&hst);
- qemu_printf("\nStatistics:\n");
- qemu_printf("TB flush count %u\n",
- qatomic_read(&tb_ctx.tb_flush_count));
- qemu_printf("TB invalidate count %u\n",
- qatomic_read(&tb_ctx.tb_phys_invalidate_count));
+ g_string_append_printf(buf, "\nStatistics:\n");
+ g_string_append_printf(buf, "TB flush count %u\n",
+ qatomic_read(&tb_ctx.tb_flush_count));
+ g_string_append_printf(buf, "TB invalidate count %u\n",
+ qatomic_read(&tb_ctx.tb_phys_invalidate_count));
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
- qemu_printf("TLB full flushes %zu\n", flush_full);
- qemu_printf("TLB partial flushes %zu\n", flush_part);
- qemu_printf("TLB elided flushes %zu\n", flush_elide);
- tcg_dump_info();
+ g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full);
+ g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part);
+ g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide);
+ tcg_dump_info(buf);
}
-void dump_opcount_info(void)
+void dump_opcount_info(GString *buf)
{
- tcg_dump_op_count();
+ tcg_dump_op_count(buf);
}
#else /* CONFIG_USER_ONLY */
ebpf_rss
vfio-migration
qapi-code-gen
- writing-qmp-commands
+ writing-monitor-commands
--- /dev/null
+How to write monitor commands
+=============================
+
+This document is a step-by-step guide on how to write new QMP commands using
+the QAPI framework and HMP commands.
+
+This document doesn't discuss QMP protocol level details, nor does it dive
+into the QAPI framework implementation.
+
+For an in-depth introduction to the QAPI framework, please refer to
+docs/devel/qapi-code-gen.txt. For documentation about the QMP protocol,
+start with docs/interop/qmp-intro.txt.
+
+New commands may be implemented in QMP only. New HMP commands should be
+implemented on top of QMP. The typical HMP command wraps around an
+equivalent QMP command, but HMP convenience commands built from QMP
+building blocks are also fine. The long term goal is to make all
+existing HMP commands conform to this, to fully isolate HMP from the
+internals of QEMU. Refer to the `Writing a debugging aid returning
+unstructured text`_ section for further guidance on commands that
+would have traditionally been HMP only.
+
+Overview
+--------
+
+Generally speaking, the following steps should be taken in order to write a
+new QMP command.
+
+1. Define the command and any types it needs in the appropriate QAPI
+ schema module.
+
+2. Write the QMP command itself, which is a regular C function. Preferably,
+ the command should be exported by some QEMU subsystem. But it can also be
+ added to the monitor/qmp-cmds.c file
+
+3. At this point the command can be tested under the QMP protocol
+
+4. Write the HMP command equivalent. This is not required and should only be
+ done if it does make sense to have the functionality in HMP. The HMP command
+ is implemented in terms of the QMP command
+
+The following sections will demonstrate each of the steps above. We will start
+very simple and get more complex as we progress.
+
+
+Testing
+-------
+
+For all the examples in the next sections, the test setup is the same and is
+shown here.
+
+First, QEMU should be started like this::
+
+ # qemu-system-TARGET [...] \
+ -chardev socket,id=qmp,port=4444,host=localhost,server=on \
+ -mon chardev=qmp,mode=control,pretty=on
+
+Then, in a different terminal::
+
+ $ telnet localhost 4444
+ Trying 127.0.0.1...
+ Connected to localhost.
+ Escape character is '^]'.
+ {
+ "QMP": {
+ "version": {
+ "qemu": {
+ "micro": 50,
+ "minor": 15,
+ "major": 0
+ },
+ "package": ""
+ },
+ "capabilities": [
+ ]
+ }
+ }
+
+The above output is the QMP server saying you're connected. The server is
+actually in capabilities negotiation mode. To enter in command mode type::
+
+ { "execute": "qmp_capabilities" }
+
+Then the server should respond::
+
+ {
+ "return": {
+ }
+ }
+
+Which is QMP's way of saying "the latest command executed OK and didn't return
+any data". Now you're ready to enter the QMP example commands as explained in
+the following sections.
+
+
+Writing a simple command: hello-world
+-------------------------------------
+
+That's the most simple QMP command that can be written. Usually, this kind of
+command carries some meaningful action in QEMU but here it will just print
+"Hello, world" to the standard output.
+
+Our command will be called "hello-world". It takes no arguments, nor does it
+return any data.
+
+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::
+
+ { 'command': 'hello-world' }
+
+The "command" keyword defines a new QMP command. It's an JSON object. All
+schema entries are JSON objects. The line above will instruct the QAPI to
+generate any prototypes and the necessary code to marshal and unmarshal
+protocol data.
+
+The next step is to write the "hello-world" implementation. As explained
+earlier, it's preferable for commands to live in QEMU subsystems. But
+"hello-world" doesn't pertain to any, so we put its implementation in
+monitor/qmp-cmds.c::
+
+ void qmp_hello_world(Error **errp)
+ {
+ printf("Hello, world!\n");
+ }
+
+There are a few things to be noticed:
+
+1. QMP command implementation functions must be prefixed with "qmp\_"
+2. qmp_hello_world() returns void, this is in accordance with the fact that the
+ command doesn't return any data
+3. It takes an "Error \*\*" argument. This is required. Later we will see how to
+ return errors and take additional arguments. The Error argument should not
+ be touched if the command doesn't return errors
+4. We won't add the function's prototype. That's automatically done by the QAPI
+5. Printing to the terminal is discouraged for QMP commands, we do it here
+ because it's the easiest way to demonstrate a QMP command
+
+You're done. Now build qemu, run it as suggested in the "Testing" section,
+and then type the following QMP command::
+
+ { "execute": "hello-world" }
+
+Then check the terminal running qemu and look for the "Hello, world" string. If
+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.
+
+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' } }
+
+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.
+
+Now, let's update our C implementation in monitor/qmp-cmds.c::
+
+ void qmp_hello_world(bool has_message, const char *message, Error **errp)
+ {
+ if (has_message) {
+ 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
+2. The C implementation signature must follow the schema's argument ordering,
+ which is defined by the "data" member
+
+Time to test our new version of the "hello-world" command. Build qemu, run it as
+described in the "Testing" section and then send two commands::
+
+ { "execute": "hello-world" }
+ {
+ "return": {
+ }
+ }
+
+ { "execute": "hello-world", "arguments": { "message": "We love qemu" } }
+ {
+ "return": {
+ }
+ }
+
+You should see "Hello, world" and "We love qemu" in the terminal running qemu,
+if you don't see these strings, then something went wrong.
+
+
+Errors
+~~~~~~
+
+QMP commands should use the error interface exported by the error.h header
+file. Basically, most errors are set by calling the error_setg() function.
+
+Let's say we don't accept the string "message" to contain the word "love". If
+it does contain it, we want the "hello-world" command to return an error::
+
+ void qmp_hello_world(bool has_message, const char *message, Error **errp)
+ {
+ if (has_message) {
+ if (strstr(message, "love")) {
+ error_setg(errp, "the word 'love' is not allowed");
+ return;
+ }
+ printf("%s\n", message);
+ } else {
+ printf("Hello, world\n");
+ }
+ }
+
+The first argument to the error_setg() function is the Error pointer
+to pointer, which is passed to all QMP functions. The next argument is a human
+description of the error, this is a free-form printf-like string.
+
+Let's test the example above. Build qemu, run it as defined in the "Testing"
+section, and then issue the following command::
+
+ { "execute": "hello-world", "arguments": { "message": "all you need is love" } }
+
+The QMP server's response should be::
+
+ {
+ "error": {
+ "class": "GenericError",
+ "desc": "the word 'love' is not allowed"
+ }
+ }
+
+Note that error_setg() produces a "GenericError" class. In general,
+all QMP errors should have that error class. There are two exceptions
+to this rule:
+
+ 1. To support a management application's need to recognize a specific
+ error for special handling
+
+ 2. Backward compatibility
+
+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
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that the QMP command is in place, we can also make it available in the human
+monitor (HMP).
+
+With the introduction of the QAPI, HMP commands make QMP calls. Most of the
+time HMP commands are simple wrappers. All HMP commands implementation exist in
+the monitor/hmp-cmds.c file.
+
+Here's the implementation of the "hello-world" HMP command::
+
+ void hmp_hello_world(Monitor *mon, const QDict *qdict)
+ {
+ const char *message = qdict_get_try_str(qdict, "message");
+ Error *err = NULL;
+
+ qmp_hello_world(!!message, message, &err);
+ if (hmp_handle_error(mon, err)) {
+ return;
+ }
+ }
+
+Also, you have to add the function's prototype to the hmp.h file.
+
+There are three 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
+ 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
+ QMP call
+
+There's one last step to actually make the command available to monitor users,
+we should add it to the hmp-commands.hx file::
+
+ {
+ .name = "hello-world",
+ .args_type = "message:s?",
+ .params = "hello-world [message]",
+ .help = "Print message to the standard output",
+ .cmd = hmp_hello_world,
+ },
+
+::
+
+ STEXI
+ @item hello_world @var{message}
+ @findex hello_world
+ Print message to the standard output
+ ETEXI
+
+To test this you have to open a user monitor and issue the "hello-world"
+command. It might be instructive to check the command's documentation with
+HMP's "help" command.
+
+Please, check the "-monitor" command-line option to know how to open a user
+monitor.
+
+
+Writing more complex commands
+-----------------------------
+
+A QMP command is capable of returning any data the QAPI supports like integers,
+strings, booleans, enumerations and user defined types.
+
+In this section we will focus on user defined types. Please, check the QAPI
+documentation for information about the other types.
+
+
+Modelling data in QAPI
+~~~~~~~~~~~~~~~~~~~~~~
+
+For a QMP command that to be considered stable and supported long term,
+there is a requirement returned data should be explicitly modelled
+using fine-grained QAPI types. As a general guide, a caller of the QMP
+command should never need to parse individual returned data fields. If
+a field appears to need parsing, then it should be split into separate
+fields corresponding to each distinct data item. This should be the
+common case for any new QMP command that is intended to be used by
+machines, as opposed to exclusively human operators.
+
+Some QMP commands, however, are only intended as ad hoc debugging aids
+for human operators. While they may return large amounts of formatted
+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.
+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
+command "x-query-registers". This returns a formatted dump of the
+architecture specific CPU state. The way the data is formatted varies
+across QEMU targets, is liable to change over time, and is only
+intended to be consumed as an opaque string by machines. Refer to the
+`Writing a debugging aid returning unstructured text`_ section for
+an illustration.
+
+User Defined Types
+~~~~~~~~~~~~~~~~~~
+
+FIXME This example needs to be redone after commit 6d32717
+
+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::
+
+ ##
+ # @QemuAlarmClock
+ #
+ # QEMU alarm clock information.
+ #
+ # @clock-name: The alarm clock method's name.
+ #
+ # @next-deadline: The time (in nanoseconds) the next alarm will fire.
+ #
+ # Since: 1.0
+ ##
+ { 'type': 'QemuAlarmClock',
+ 'data': { 'clock-name': 'str', '*next-deadline': '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.
+
+Now let's define the query-alarm-clock command::
+
+ ##
+ # @query-alarm-clock
+ #
+ # Return information about QEMU's alarm clock.
+ #
+ # Returns a @QemuAlarmClock instance describing the alarm clock method
+ # being currently used by QEMU (this is usually set by the '-clock'
+ # command-line option).
+ #
+ # Since: 1.0
+ ##
+ { 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' }
+
+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::
+
+ QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
+ {
+ QemuAlarmClock *clock;
+ int64_t deadline;
+
+ clock = g_malloc0(sizeof(*clock));
+
+ deadline = qemu_next_alarm_deadline();
+ if (deadline > 0) {
+ clock->has_next_deadline = true;
+ clock->next_deadline = deadline;
+ }
+ clock->clock_name = g_strdup(alarm_timer->name);
+
+ return clock;
+ }
+
+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? All optional members have a
+ 'has_TYPE_NAME' member that should be properly 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" }
+ {
+ "return": {
+ "next-deadline": 2368219,
+ "clock-name": "dynticks"
+ }
+ }
+
+
+The HMP command
+~~~~~~~~~~~~~~~
+
+Here's the HMP counterpart of the query-alarm-clock command::
+
+ void hmp_info_alarm_clock(Monitor *mon)
+ {
+ QemuAlarmClock *clock;
+ Error *err = NULL;
+
+ clock = qmp_query_alarm_clock(&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"
+ }
+ ]
+ }
+
+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
+---------------------------------------------------
+
+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.
+
+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' } }
+
+ { 'command': 'x-query-roms',
+ 'returns': 'HumanReadableText' }
+
+The ``HumanReadableText`` struct is intended to be used for all
+commands, under the ``x-`` name prefix that are returning unstructured
+text targetted at humans. It should never be used for commands outside
+the ``x-`` name prefix, as those should be using structured QAPI types.
+
+Implementing the QMP command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The QMP implementation will typically involve creating a ``GString``
+object and printing formatted data into it::
+
+ HumanReadableText *qmp_x_query_roms(Error **errp)
+ {
+ g_autoptr(GString) buf = g_string_new("");
+ Rom *rom;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ g_string_append_printf("%s size=0x%06zx name=\"%s\"\n",
+ memory_region_name(rom->mr),
+ rom->romsize,
+ rom->name);
+ }
+
+ return human_readable_text_from_str(buf);
+ }
+
+
+Implementing the HMP command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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::
+
+ void hmp_info_roms(Monitor *mon, const QDict *qdict)
+ {
+ Error err = NULL;
+ g_autoptr(HumanReadableText) info = qmp_x_query_roms(&err);
+
+ if (hmp_handle_error(mon, err)) {
+ return;
+ }
+ monitor_printf(mon, "%s", info->human_readable_text);
+ }
+
+Also, you have to add the function's prototype to the hmp.h file.
+
+There's one last step to actually make the command available to
+monitor users, we should add it to the hmp-commands-info.hx file::
+
+ {
+ .name = "roms",
+ .args_type = "",
+ .params = "",
+ .help = "show roms",
+ .cmd = hmp_info_roms,
+ },
+
+The case of writing a HMP info handler that calls a no-parameter QMP query
+command is quite common. To simplify the implementation there is a general
+purpose HMP info handler for this scenario. All that is required to expose
+a no-parameter QMP query command via HMP is to declare it using the
+'.cmd_info_hrt' field to point to the QMP handler, and leave the '.cmd'
+field NULL::
+
+ {
+ .name = "roms",
+ .args_type = "",
+ .params = "",
+ .help = "show roms",
+ .cmd_info_hrt = qmp_x_query_roms,
+ },
+++ /dev/null
-How to write QMP commands using the QAPI framework
-==================================================
-
-This document is a step-by-step guide on how to write new QMP commands using
-the QAPI framework. It also shows how to implement new style HMP commands.
-
-This document doesn't discuss QMP protocol level details, nor does it dive
-into the QAPI framework implementation.
-
-For an in-depth introduction to the QAPI framework, please refer to
-docs/devel/qapi-code-gen.txt. For documentation about the QMP protocol,
-start with docs/interop/qmp-intro.txt.
-
-
-Overview
---------
-
-Generally speaking, the following steps should be taken in order to write a
-new QMP command.
-
-1. Define the command and any types it needs in the appropriate QAPI
- schema module.
-
-2. Write the QMP command itself, which is a regular C function. Preferably,
- the command should be exported by some QEMU subsystem. But it can also be
- added to the monitor/qmp-cmds.c file
-
-3. At this point the command can be tested under the QMP protocol
-
-4. Write the HMP command equivalent. This is not required and should only be
- done if it does make sense to have the functionality in HMP. The HMP command
- is implemented in terms of the QMP command
-
-The following sections will demonstrate each of the steps above. We will start
-very simple and get more complex as we progress.
-
-
-Testing
--------
-
-For all the examples in the next sections, the test setup is the same and is
-shown here.
-
-First, QEMU should be started like this::
-
- # qemu-system-TARGET [...] \
- -chardev socket,id=qmp,port=4444,host=localhost,server=on \
- -mon chardev=qmp,mode=control,pretty=on
-
-Then, in a different terminal::
-
- $ telnet localhost 4444
- Trying 127.0.0.1...
- Connected to localhost.
- Escape character is '^]'.
- {
- "QMP": {
- "version": {
- "qemu": {
- "micro": 50,
- "minor": 15,
- "major": 0
- },
- "package": ""
- },
- "capabilities": [
- ]
- }
- }
-
-The above output is the QMP server saying you're connected. The server is
-actually in capabilities negotiation mode. To enter in command mode type::
-
- { "execute": "qmp_capabilities" }
-
-Then the server should respond::
-
- {
- "return": {
- }
- }
-
-Which is QMP's way of saying "the latest command executed OK and didn't return
-any data". Now you're ready to enter the QMP example commands as explained in
-the following sections.
-
-
-Writing a command that doesn't return data
-------------------------------------------
-
-That's the most simple QMP command that can be written. Usually, this kind of
-command carries some meaningful action in QEMU but here it will just print
-"Hello, world" to the standard output.
-
-Our command will be called "hello-world". It takes no arguments, nor does it
-return any data.
-
-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::
-
- { 'command': 'hello-world' }
-
-The "command" keyword defines a new QMP command. It's an JSON object. All
-schema entries are JSON objects. The line above will instruct the QAPI to
-generate any prototypes and the necessary code to marshal and unmarshal
-protocol data.
-
-The next step is to write the "hello-world" implementation. As explained
-earlier, it's preferable for commands to live in QEMU subsystems. But
-"hello-world" doesn't pertain to any, so we put its implementation in
-monitor/qmp-cmds.c::
-
- void qmp_hello_world(Error **errp)
- {
- printf("Hello, world!\n");
- }
-
-There are a few things to be noticed:
-
-1. QMP command implementation functions must be prefixed with "qmp\_"
-2. qmp_hello_world() returns void, this is in accordance with the fact that the
- command doesn't return any data
-3. It takes an "Error \*\*" argument. This is required. Later we will see how to
- return errors and take additional arguments. The Error argument should not
- be touched if the command doesn't return errors
-4. We won't add the function's prototype. That's automatically done by the QAPI
-5. Printing to the terminal is discouraged for QMP commands, we do it here
- because it's the easiest way to demonstrate a QMP command
-
-You're done. Now build qemu, run it as suggested in the "Testing" section,
-and then type the following QMP command::
-
- { "execute": "hello-world" }
-
-Then check the terminal running qemu and look for the "Hello, world" string. If
-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.
-
-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' } }
-
-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.
-
-Now, let's update our C implementation in monitor/qmp-cmds.c::
-
- void qmp_hello_world(bool has_message, const char *message, Error **errp)
- {
- if (has_message) {
- 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
-2. The C implementation signature must follow the schema's argument ordering,
- which is defined by the "data" member
-
-Time to test our new version of the "hello-world" command. Build qemu, run it as
-described in the "Testing" section and then send two commands::
-
- { "execute": "hello-world" }
- {
- "return": {
- }
- }
-
- { "execute": "hello-world", "arguments": { "message": "We love qemu" } }
- {
- "return": {
- }
- }
-
-You should see "Hello, world" and "We love qemu" in the terminal running qemu,
-if you don't see these strings, then something went wrong.
-
-
-Errors
-~~~~~~
-
-QMP commands should use the error interface exported by the error.h header
-file. Basically, most errors are set by calling the error_setg() function.
-
-Let's say we don't accept the string "message" to contain the word "love". If
-it does contain it, we want the "hello-world" command to return an error::
-
- void qmp_hello_world(bool has_message, const char *message, Error **errp)
- {
- if (has_message) {
- if (strstr(message, "love")) {
- error_setg(errp, "the word 'love' is not allowed");
- return;
- }
- printf("%s\n", message);
- } else {
- printf("Hello, world\n");
- }
- }
-
-The first argument to the error_setg() function is the Error pointer
-to pointer, which is passed to all QMP functions. The next argument is a human
-description of the error, this is a free-form printf-like string.
-
-Let's test the example above. Build qemu, run it as defined in the "Testing"
-section, and then issue the following command::
-
- { "execute": "hello-world", "arguments": { "message": "all you need is love" } }
-
-The QMP server's response should be::
-
- {
- "error": {
- "class": "GenericError",
- "desc": "the word 'love' is not allowed"
- }
- }
-
-Note that error_setg() produces a "GenericError" class. In general,
-all QMP errors should have that error class. There are two exceptions
-to this rule:
-
- 1. To support a management application's need to recognize a specific
- error for special handling
-
- 2. Backward compatibility
-
-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
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Now that the QMP command is in place, we can also make it available in the human
-monitor (HMP).
-
-With the introduction of the QAPI, HMP commands make QMP calls. Most of the
-time HMP commands are simple wrappers. All HMP commands implementation exist in
-the monitor/hmp-cmds.c file.
-
-Here's the implementation of the "hello-world" HMP command::
-
- void hmp_hello_world(Monitor *mon, const QDict *qdict)
- {
- const char *message = qdict_get_try_str(qdict, "message");
- Error *err = NULL;
-
- qmp_hello_world(!!message, message, &err);
- if (err) {
- monitor_printf(mon, "%s\n", error_get_pretty(err));
- error_free(err);
- return;
- }
- }
-
-Also, you have to add the function's prototype to the hmp.h file.
-
-There are three 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 print
- the error description 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
- QMP call
-
-There's one last step to actually make the command available to monitor users,
-we should add it to the hmp-commands.hx file::
-
- {
- .name = "hello-world",
- .args_type = "message:s?",
- .params = "hello-world [message]",
- .help = "Print message to the standard output",
- .cmd = hmp_hello_world,
- },
-
-::
-
- STEXI
- @item hello_world @var{message}
- @findex hello_world
- Print message to the standard output
- ETEXI
-
-To test this you have to open a user monitor and issue the "hello-world"
-command. It might be instructive to check the command's documentation with
-HMP's "help" command.
-
-Please, check the "-monitor" command-line option to know how to open a user
-monitor.
-
-
-Writing a command that returns data
------------------------------------
-
-A QMP command is capable of returning any data the QAPI supports like integers,
-strings, booleans, enumerations and user defined types.
-
-In this section we will focus on user defined types. Please, check the QAPI
-documentation for information about the other types.
-
-
-User Defined Types
-~~~~~~~~~~~~~~~~~~
-
-FIXME This example needs to be redone after commit 6d32717
-
-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::
-
- ##
- # @QemuAlarmClock
- #
- # QEMU alarm clock information.
- #
- # @clock-name: The alarm clock method's name.
- #
- # @next-deadline: The time (in nanoseconds) the next alarm will fire.
- #
- # Since: 1.0
- ##
- { 'type': 'QemuAlarmClock',
- 'data': { 'clock-name': 'str', '*next-deadline': '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.
-
-Now let's define the query-alarm-clock command::
-
- ##
- # @query-alarm-clock
- #
- # Return information about QEMU's alarm clock.
- #
- # Returns a @QemuAlarmClock instance describing the alarm clock method
- # being currently used by QEMU (this is usually set by the '-clock'
- # command-line option).
- #
- # Since: 1.0
- ##
- { 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' }
-
-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::
-
- QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
- {
- QemuAlarmClock *clock;
- int64_t deadline;
-
- clock = g_malloc0(sizeof(*clock));
-
- deadline = qemu_next_alarm_deadline();
- if (deadline > 0) {
- clock->has_next_deadline = true;
- clock->next_deadline = deadline;
- }
- clock->clock_name = g_strdup(alarm_timer->name);
-
- return clock;
- }
-
-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? All optional members have a
- 'has_TYPE_NAME' member that should be properly 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" }
- {
- "return": {
- "next-deadline": 2368219,
- "clock-name": "dynticks"
- }
- }
-
-
-The HMP command
-~~~~~~~~~~~~~~~
-
-Here's the HMP counterpart of the query-alarm-clock command::
-
- void hmp_info_alarm_clock(Monitor *mon)
- {
- QemuAlarmClock *clock;
- Error *err = NULL;
-
- clock = qmp_query_alarm_clock(&err);
- if (err) {
- monitor_printf(mon, "Could not query alarm clock information\n");
- error_free(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"
- }
- ]
- }
-
-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 (err) {
- monitor_printf(mon, "Could not query alarm methods\n");
- error_free(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);
- }
Show local APIC state
ERST
-#if defined(TARGET_I386)
- {
- .name = "ioapic",
- .args_type = "",
- .params = "",
- .help = "show io apic state",
- .cmd = hmp_info_io_apic,
- },
-#endif
-
-SRST
- ``info ioapic``
- Show io APIC state
-ERST
-
{
.name = "cpus",
.args_type = "",
.args_type = "",
.params = "",
.help = "show the interrupts statistics (if available)",
- .cmd = hmp_info_irq,
+ .cmd_info_hrt = qmp_x_query_irq,
},
SRST
.args_type = "",
.params = "",
.help = "show RDMA state",
- .cmd = hmp_info_rdma,
+ .cmd_info_hrt = qmp_x_query_rdma,
},
SRST
.args_type = "",
.params = "",
.help = "show NUMA information",
- .cmd = hmp_info_numa,
+ .cmd_info_hrt = qmp_x_query_numa,
},
SRST
.args_type = "",
.params = "",
.help = "show guest USB devices",
- .cmd = hmp_info_usb,
+ .cmd_info_hrt = qmp_x_query_usb,
},
SRST
.args_type = "",
.params = "",
.help = "show profiling information",
- .cmd = hmp_info_profile,
+ .cmd_info_hrt = qmp_x_query_profile,
},
SRST
.args_type = "",
.params = "",
.help = "show roms",
- .cmd = hmp_info_roms,
+ .cmd_info_hrt = qmp_x_query_roms,
},
SRST
.args_type = "",
.params = "",
.help = "Display system ramblock information",
- .cmd = hmp_info_ramblock,
+ .cmd_info_hrt = qmp_x_query_ramblock,
},
SRST
#include "qemu-common.h"
#include "qemu/datadir.h"
#include "qapi/error.h"
+#include "qapi/qapi-commands-machine.h"
+#include "qapi/type-helpers.h"
#include "trace.h"
#include "hw/hw.h"
#include "disas/disas.h"
return cbdata.rom;
}
-void hmp_info_roms(Monitor *mon, const QDict *qdict)
+HumanReadableText *qmp_x_query_roms(Error **errp)
{
Rom *rom;
+ g_autoptr(GString) buf = g_string_new("");
QTAILQ_FOREACH(rom, &roms, next) {
if (rom->mr) {
- monitor_printf(mon, "%s"
- " size=0x%06zx name=\"%s\"\n",
- memory_region_name(rom->mr),
- rom->romsize,
- rom->name);
+ g_string_append_printf(buf, "%s"
+ " size=0x%06zx name=\"%s\"\n",
+ memory_region_name(rom->mr),
+ rom->romsize,
+ rom->name);
} else if (!rom->fw_file) {
- monitor_printf(mon, "addr=" TARGET_FMT_plx
- " size=0x%06zx mem=%s name=\"%s\"\n",
- rom->addr, rom->romsize,
- rom->isrom ? "rom" : "ram",
- rom->name);
+ g_string_append_printf(buf, "addr=" TARGET_FMT_plx
+ " size=0x%06zx mem=%s name=\"%s\"\n",
+ rom->addr, rom->romsize,
+ rom->isrom ? "rom" : "ram",
+ rom->name);
} else {
- monitor_printf(mon, "fw=%s/%s"
- " size=0x%06zx name=\"%s\"\n",
- rom->fw_dir,
- rom->fw_file,
- rom->romsize,
- rom->name);
+ g_string_append_printf(buf, "fw=%s/%s"
+ " size=0x%06zx name=\"%s\"\n",
+ rom->fw_dir,
+ rom->fw_file,
+ rom->romsize,
+ rom->name);
}
}
+
+ return human_readable_text_from_str(buf);
}
typedef enum HexRecord HexRecord;
HotpluggableCPUList *saved = l;
CpuInstanceProperties *c;
- if (err != NULL) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
qapi_free_MemdevList(memdev_list);
hmp_handle_error(mon, err);
}
-
-void hmp_info_numa(Monitor *mon, const QDict *qdict)
-{
- int i, nb_numa_nodes;
- NumaNodeMem *node_mem;
- CpuInfoFastList *cpu_list, *cpu;
- MachineState *ms = MACHINE(qdev_get_machine());
-
- nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
- monitor_printf(mon, "%d nodes\n", nb_numa_nodes);
- if (!nb_numa_nodes) {
- return;
- }
-
- cpu_list = qmp_query_cpus_fast(&error_abort);
- node_mem = g_new0(NumaNodeMem, nb_numa_nodes);
-
- query_numa_node_mem(node_mem, ms);
- for (i = 0; i < nb_numa_nodes; i++) {
- monitor_printf(mon, "node %d cpus:", i);
- for (cpu = cpu_list; cpu; cpu = cpu->next) {
- if (cpu->value->has_props && cpu->value->props->has_node_id &&
- cpu->value->props->node_id == i) {
- monitor_printf(mon, " %" PRIi64, cpu->value->cpu_index);
- }
- }
- monitor_printf(mon, "\n");
- monitor_printf(mon, "node %d size: %" PRId64 " MB\n", i,
- node_mem[i].node_mem >> 20);
- monitor_printf(mon, "node %d plugged: %" PRId64 " MB\n", i,
- node_mem[i].node_plugged_mem >> 20);
- }
- qapi_free_CpuInfoFastList(cpu_list);
- g_free(node_mem);
-}
#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qobject.h"
#include "qapi/qobject-input-visitor.h"
+#include "qapi/type-helpers.h"
#include "qemu/main-loop.h"
#include "qom/qom-qobject.h"
#include "sysemu/hostmem.h"
object_child_foreach(obj, query_memdev, &list);
return list;
}
+
+HumanReadableText *qmp_x_query_numa(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+ int i, nb_numa_nodes;
+ NumaNodeMem *node_mem;
+ CpuInfoFastList *cpu_list, *cpu;
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
+ g_string_append_printf(buf, "%d nodes\n", nb_numa_nodes);
+ if (!nb_numa_nodes) {
+ goto done;
+ }
+
+ cpu_list = qmp_query_cpus_fast(&error_abort);
+ node_mem = g_new0(NumaNodeMem, nb_numa_nodes);
+
+ query_numa_node_mem(node_mem, ms);
+ for (i = 0; i < nb_numa_nodes; i++) {
+ g_string_append_printf(buf, "node %d cpus:", i);
+ for (cpu = cpu_list; cpu; cpu = cpu->next) {
+ if (cpu->value->has_props && cpu->value->props->has_node_id &&
+ cpu->value->props->node_id == i) {
+ g_string_append_printf(buf, " %" PRIi64, cpu->value->cpu_index);
+ }
+ }
+ g_string_append_printf(buf, "\n");
+ g_string_append_printf(buf, "node %d size: %" PRId64 " MB\n", i,
+ node_mem[i].node_mem >> 20);
+ g_string_append_printf(buf, "node %d plugged: %" PRId64 " MB\n", i,
+ node_mem[i].node_plugged_mem >> 20);
+ }
+ qapi_free_CpuInfoFastList(cpu_list);
+ g_free(node_mem);
+
+ done:
+ return human_readable_text_from_str(buf);
+}
#define PG_DIR_SZ { TARGET_PAGE_SIZE / sizeof(__u64) }
#define PG_TBL_SZ { TARGET_PAGE_SIZE / sizeof(__u64) }
-void rdma_dump_device_counters(Monitor *mon, RdmaDeviceResources *dev_res)
-{
- monitor_printf(mon, "\ttx : %" PRId64 "\n",
- dev_res->stats.tx);
- monitor_printf(mon, "\ttx_len : %" PRId64 "\n",
- dev_res->stats.tx_len);
- monitor_printf(mon, "\ttx_err : %" PRId64 "\n",
- dev_res->stats.tx_err);
- monitor_printf(mon, "\trx_bufs : %" PRId64 "\n",
- dev_res->stats.rx_bufs);
- monitor_printf(mon, "\trx_srq : %" PRId64 "\n",
- dev_res->stats.rx_srq);
- monitor_printf(mon, "\trx_bufs_len : %" PRId64 "\n",
- dev_res->stats.rx_bufs_len);
- monitor_printf(mon, "\trx_bufs_err : %" PRId64 "\n",
- dev_res->stats.rx_bufs_err);
- monitor_printf(mon, "\tcomps : %" PRId64 "\n",
- dev_res->stats.completions);
- monitor_printf(mon, "\tmissing_comps : %" PRId32 "\n",
- dev_res->stats.missing_cqe);
- monitor_printf(mon, "\tpoll_cq (bk) : %" PRId64 "\n",
- dev_res->stats.poll_cq_from_bk);
- monitor_printf(mon, "\tpoll_cq_ppoll_to : %" PRId64 "\n",
- dev_res->stats.poll_cq_ppoll_to);
- monitor_printf(mon, "\tpoll_cq (fe) : %" PRId64 "\n",
- dev_res->stats.poll_cq_from_guest);
- monitor_printf(mon, "\tpoll_cq_empty : %" PRId64 "\n",
- dev_res->stats.poll_cq_from_guest_empty);
- monitor_printf(mon, "\tmad_tx : %" PRId64 "\n",
- dev_res->stats.mad_tx);
- monitor_printf(mon, "\tmad_tx_err : %" PRId64 "\n",
- dev_res->stats.mad_tx_err);
- monitor_printf(mon, "\tmad_rx : %" PRId64 "\n",
- dev_res->stats.mad_rx);
- monitor_printf(mon, "\tmad_rx_err : %" PRId64 "\n",
- dev_res->stats.mad_rx_err);
- monitor_printf(mon, "\tmad_rx_bufs : %" PRId64 "\n",
- dev_res->stats.mad_rx_bufs);
- monitor_printf(mon, "\tmad_rx_bufs_err : %" PRId64 "\n",
- dev_res->stats.mad_rx_bufs_err);
- monitor_printf(mon, "\tPDs : %" PRId32 "\n",
- dev_res->pd_tbl.used);
- monitor_printf(mon, "\tMRs : %" PRId32 "\n",
- dev_res->mr_tbl.used);
- monitor_printf(mon, "\tUCs : %" PRId32 "\n",
- dev_res->uc_tbl.used);
- monitor_printf(mon, "\tQPs : %" PRId32 "\n",
- dev_res->qp_tbl.used);
- monitor_printf(mon, "\tCQs : %" PRId32 "\n",
- dev_res->cq_tbl.used);
- monitor_printf(mon, "\tCEQ_CTXs : %" PRId32 "\n",
- dev_res->cqe_ctx_tbl.used);
+void rdma_format_device_counters(RdmaDeviceResources *dev_res, GString *buf)
+{
+ g_string_append_printf(buf, "\ttx : %" PRId64 "\n",
+ dev_res->stats.tx);
+ g_string_append_printf(buf, "\ttx_len : %" PRId64 "\n",
+ dev_res->stats.tx_len);
+ g_string_append_printf(buf, "\ttx_err : %" PRId64 "\n",
+ dev_res->stats.tx_err);
+ g_string_append_printf(buf, "\trx_bufs : %" PRId64 "\n",
+ dev_res->stats.rx_bufs);
+ g_string_append_printf(buf, "\trx_srq : %" PRId64 "\n",
+ dev_res->stats.rx_srq);
+ g_string_append_printf(buf, "\trx_bufs_len : %" PRId64 "\n",
+ dev_res->stats.rx_bufs_len);
+ g_string_append_printf(buf, "\trx_bufs_err : %" PRId64 "\n",
+ dev_res->stats.rx_bufs_err);
+ g_string_append_printf(buf, "\tcomps : %" PRId64 "\n",
+ dev_res->stats.completions);
+ g_string_append_printf(buf, "\tmissing_comps : %" PRId32 "\n",
+ dev_res->stats.missing_cqe);
+ g_string_append_printf(buf, "\tpoll_cq (bk) : %" PRId64 "\n",
+ dev_res->stats.poll_cq_from_bk);
+ g_string_append_printf(buf, "\tpoll_cq_ppoll_to : %" PRId64 "\n",
+ dev_res->stats.poll_cq_ppoll_to);
+ g_string_append_printf(buf, "\tpoll_cq (fe) : %" PRId64 "\n",
+ dev_res->stats.poll_cq_from_guest);
+ g_string_append_printf(buf, "\tpoll_cq_empty : %" PRId64 "\n",
+ dev_res->stats.poll_cq_from_guest_empty);
+ g_string_append_printf(buf, "\tmad_tx : %" PRId64 "\n",
+ dev_res->stats.mad_tx);
+ g_string_append_printf(buf, "\tmad_tx_err : %" PRId64 "\n",
+ dev_res->stats.mad_tx_err);
+ g_string_append_printf(buf, "\tmad_rx : %" PRId64 "\n",
+ dev_res->stats.mad_rx);
+ g_string_append_printf(buf, "\tmad_rx_err : %" PRId64 "\n",
+ dev_res->stats.mad_rx_err);
+ g_string_append_printf(buf, "\tmad_rx_bufs : %" PRId64 "\n",
+ dev_res->stats.mad_rx_bufs);
+ g_string_append_printf(buf, "\tmad_rx_bufs_err : %" PRId64 "\n",
+ dev_res->stats.mad_rx_bufs_err);
+ g_string_append_printf(buf, "\tPDs : %" PRId32 "\n",
+ dev_res->pd_tbl.used);
+ g_string_append_printf(buf, "\tMRs : %" PRId32 "\n",
+ dev_res->mr_tbl.used);
+ g_string_append_printf(buf, "\tUCs : %" PRId32 "\n",
+ dev_res->uc_tbl.used);
+ g_string_append_printf(buf, "\tQPs : %" PRId32 "\n",
+ dev_res->qp_tbl.used);
+ g_string_append_printf(buf, "\tCQs : %" PRId32 "\n",
+ dev_res->cq_tbl.used);
+ g_string_append_printf(buf, "\tCEQ_CTXs : %" PRId32 "\n",
+ dev_res->cqe_ctx_tbl.used);
}
static inline void res_tbl_init(const char *name, RdmaRmResTbl *tbl,
{
return &dev_res->port.gid_tbl[sgid_idx].gid;
}
-void rdma_dump_device_counters(Monitor *mon, RdmaDeviceResources *dev_res);
+void rdma_format_device_counters(RdmaDeviceResources *dev_res, GString *buf);
#endif
DEFINE_PROP_END_OF_LIST(),
};
-static void pvrdma_print_statistics(Monitor *mon, RdmaProvider *obj)
+static void pvrdma_format_statistics(RdmaProvider *obj, GString *buf)
{
PVRDMADev *dev = PVRDMA_DEV(obj);
PCIDevice *pdev = PCI_DEVICE(dev);
- monitor_printf(mon, "%s, %x.%x\n", pdev->name, PCI_SLOT(pdev->devfn),
- PCI_FUNC(pdev->devfn));
- monitor_printf(mon, "\tcommands : %" PRId64 "\n",
- dev->stats.commands);
- monitor_printf(mon, "\tregs_reads : %" PRId64 "\n",
- dev->stats.regs_reads);
- monitor_printf(mon, "\tregs_writes : %" PRId64 "\n",
- dev->stats.regs_writes);
- monitor_printf(mon, "\tuar_writes : %" PRId64 "\n",
- dev->stats.uar_writes);
- monitor_printf(mon, "\tinterrupts : %" PRId64 "\n",
- dev->stats.interrupts);
- rdma_dump_device_counters(mon, &dev->rdma_dev_res);
+ g_string_append_printf(buf, "%s, %x.%x\n",
+ pdev->name, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn));
+ g_string_append_printf(buf, "\tcommands : %" PRId64 "\n",
+ dev->stats.commands);
+ g_string_append_printf(buf, "\tregs_reads : %" PRId64 "\n",
+ dev->stats.regs_reads);
+ g_string_append_printf(buf, "\tregs_writes : %" PRId64 "\n",
+ dev->stats.regs_writes);
+ g_string_append_printf(buf, "\tuar_writes : %" PRId64 "\n",
+ dev->stats.uar_writes);
+ g_string_append_printf(buf, "\tinterrupts : %" PRId64 "\n",
+ dev->stats.interrupts);
+ rdma_format_device_counters(&dev->rdma_dev_res, buf);
}
static void free_dev_ring(PCIDevice *pci_dev, PvrdmaRing *ring,
device_class_set_props(dc, pvrdma_dev_properties);
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
- ir->print_statistics = pvrdma_print_statistics;
+ ir->format_statistics = pvrdma_format_statistics;
}
static const TypeInfo pvrdma_info = {
#include "hw/qdev-properties.h"
#include "hw/usb.h"
#include "qapi/error.h"
+#include "qapi/qapi-commands-machine.h"
+#include "qapi/type-helpers.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "sysemu/sysemu.h"
return fw_path;
}
-void hmp_info_usb(Monitor *mon, const QDict *qdict)
+HumanReadableText *qmp_x_query_usb(Error **errp)
{
+ g_autoptr(GString) buf = g_string_new("");
USBBus *bus;
USBDevice *dev;
USBPort *port;
if (QTAILQ_EMPTY(&busses)) {
- monitor_printf(mon, "USB support not enabled\n");
- return;
+ error_setg(errp, "USB support not enabled");
+ return NULL;
}
QTAILQ_FOREACH(bus, &busses, next) {
dev = port->dev;
if (!dev)
continue;
- monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, "
- "Product %s%s%s\n",
- bus->busnr, dev->addr, port->path,
- usb_speed(dev->speed), dev->product_desc,
- dev->qdev.id ? ", ID: " : "",
- dev->qdev.id ?: "");
+ g_string_append_printf(buf,
+ " Device %d.%d, Port %s, Speed %s Mb/s, "
+ "Product %s%s%s\n",
+ bus->busnr, dev->addr, port->path,
+ usb_speed(dev->speed), dev->product_desc,
+ dev->qdev.id ? ", ID: " : "",
+ dev->qdev.id ?: "");
}
}
+
+ return human_readable_text_from_str(buf);
}
/* handle legacy -usbdevice cmd line option */
#ifdef CONFIG_TCG
/* accel/tcg/cpu-exec.c */
-void dump_drift_info(void);
+void dump_drift_info(GString *buf);
/* accel/tcg/translate-all.c */
-void dump_exec_info(void);
-void dump_opcount_info(void);
+void dump_exec_info(GString *buf);
+void dump_opcount_info(GString *buf);
#endif /* CONFIG_TCG */
#endif /* !CONFIG_USER_ONLY */
void ram_block_notify_remove(void *host, size_t size, size_t max_size);
void ram_block_notify_resize(void *host, size_t old_size, size_t new_size);
-void ram_block_dump(Monitor *mon);
+GString *ram_block_format(void);
#endif /* RAMLIST_H */
struct RdmaProviderClass {
InterfaceClass parent;
- void (*print_statistics)(Monitor *mon, RdmaProvider *obj);
+ void (*format_statistics)(RdmaProvider *obj, GString *buf);
};
#endif
void hmp_info_tlb(Monitor *mon, const QDict *qdict);
void hmp_mce(Monitor *mon, const QDict *qdict);
void hmp_info_local_apic(Monitor *mon, const QDict *qdict);
-void hmp_info_io_apic(Monitor *mon, const QDict *qdict);
void hmp_info_sev(Monitor *mon, const QDict *qdict);
void hmp_info_sgx(Monitor *mon, const QDict *qdict);
#define HMP_H
#include "qemu/readline.h"
+#include "qapi/qapi-types-common.h"
-void hmp_handle_error(Monitor *mon, Error *err);
+bool hmp_handle_error(Monitor *mon, Error *err);
void hmp_info_name(Monitor *mon, const QDict *qdict);
void hmp_info_version(Monitor *mon, const QDict *qdict);
void hmp_replay_seek(Monitor *mon, const QDict *qdict);
void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict);
void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict);
+void hmp_human_readable_text_helper(Monitor *mon,
+ HumanReadableText *(*qmp_handler)(Error **));
#endif
void monitor_register_hmp(const char *name, bool info,
void (*cmd)(Monitor *mon, const QDict *qdict));
+void monitor_register_hmp_info_hrt(const char *name,
+ HumanReadableText *(*handler)(Error **errp));
#endif /* MONITOR_H */
--- /dev/null
+/*
+ * QAPI common helper functions
+ *
+ * This file provides helper functions related to types defined
+ * in the QAPI schema.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/qapi-types-common.h"
+
+HumanReadableText *human_readable_text_from_str(GString *str);
#endif
int64_t tcg_cpu_exec_time(void);
-void tcg_dump_info(void);
-void tcg_dump_op_count(void);
+void tcg_dump_info(GString *buf);
+void tcg_dump_op_count(GString *buf);
#define TCG_CT_CONST 1 /* any constant of register size */
#include "ui/console.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
-#include "exec/ramlist.h"
#include "hw/intc/intc.h"
-#include "hw/rdma/rdma.h"
#include "migration/snapshot.h"
#include "migration/misc.h"
#include <spice/enums.h>
#endif
-void hmp_handle_error(Monitor *mon, Error *err)
+bool hmp_handle_error(Monitor *mon, Error *err)
{
if (err) {
error_reportf_err(err, "Error: ");
+ return true;
}
+ return false;
}
/*
info2l = qmp_query_vnc_servers(&err);
info2l_head = info2l;
- if (err) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
if (!info2l) {
Error *err = NULL;
info = qmp_query_balloon(&err);
- if (err) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
}
}
-static int hmp_info_irq_foreach(Object *obj, void *opaque)
-{
- InterruptStatsProvider *intc;
- InterruptStatsProviderClass *k;
- Monitor *mon = opaque;
-
- if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) {
- intc = INTERRUPT_STATS_PROVIDER(obj);
- k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj);
- uint64_t *irq_counts;
- unsigned int nb_irqs, i;
- if (k->get_statistics &&
- k->get_statistics(intc, &irq_counts, &nb_irqs)) {
- if (nb_irqs > 0) {
- monitor_printf(mon, "IRQ statistics for %s:\n",
- object_get_typename(obj));
- for (i = 0; i < nb_irqs; i++) {
- if (irq_counts[i] > 0) {
- monitor_printf(mon, "%2d: %" PRId64 "\n", i,
- irq_counts[i]);
- }
- }
- }
- } else {
- monitor_printf(mon, "IRQ statistics not available for %s.\n",
- object_get_typename(obj));
- }
- }
-
- return 0;
-}
-
-void hmp_info_irq(Monitor *mon, const QDict *qdict)
-{
- object_child_foreach_recursive(object_get_root(),
- hmp_info_irq_foreach, mon);
-}
-
static int hmp_info_pic_foreach(Object *obj, void *opaque)
{
InterruptStatsProvider *intc;
hmp_info_pic_foreach, mon);
}
-static int hmp_info_rdma_foreach(Object *obj, void *opaque)
-{
- RdmaProvider *rdma;
- RdmaProviderClass *k;
- Monitor *mon = opaque;
-
- if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) {
- rdma = RDMA_PROVIDER(obj);
- k = RDMA_PROVIDER_GET_CLASS(obj);
- if (k->print_statistics) {
- k->print_statistics(mon, rdma);
- } else {
- monitor_printf(mon, "RDMA statistics not available for %s.\n",
- object_get_typename(obj));
- }
- }
-
- return 0;
-}
-
-void hmp_info_rdma(Monitor *mon, const QDict *qdict)
-{
- object_child_foreach_recursive(object_get_root(),
- hmp_info_rdma_foreach, mon);
-}
-
void hmp_info_pci(Monitor *mon, const QDict *qdict)
{
PciInfoList *info_list, *info;
int i;
data = qmp_ringbuf_read(chardev, size, false, 0, &err);
- if (err) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
qmp_migrate(uri, !!blk, blk, !!inc, inc,
false, false, true, resume, &err);
- if (err) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
Error *err = NULL;
rocker = qmp_query_rocker(name, &err);
- if (err != NULL) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
Error *err = NULL;
list = qmp_query_rocker_ports(name, &err);
- if (err != NULL) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
Error *err = NULL;
list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &err);
- if (err != NULL) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
Error *err = NULL;
list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &err);
- if (err != NULL) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
qapi_free_RockerOfDpaGroupList(list);
}
-void hmp_info_ramblock(Monitor *mon, const QDict *qdict)
-{
- ram_block_dump(mon);
-}
-
void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
#include <dirent.h>
#include "hw/qdev-core.h"
#include "monitor-internal.h"
+#include "monitor/hmp.h"
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qnum.h"
return NULL;
}
+static void hmp_info_human_readable_text(Monitor *mon,
+ HumanReadableText *(*handler)(Error **))
+{
+ Error *err = NULL;
+ g_autoptr(HumanReadableText) info = handler(&err);
+
+ if (hmp_handle_error(mon, err)) {
+ return;
+ }
+
+ monitor_printf(mon, "%s", info->human_readable_text);
+}
+
+static void handle_hmp_command_exec(Monitor *mon,
+ const HMPCommand *cmd,
+ QDict *qdict)
+{
+ if (cmd->cmd_info_hrt) {
+ hmp_info_human_readable_text(mon,
+ cmd->cmd_info_hrt);
+ } else {
+ cmd->cmd(mon, qdict);
+ }
+}
+
typedef struct HandleHmpCommandCo {
Monitor *mon;
const HMPCommand *cmd;
static void handle_hmp_command_co(void *opaque)
{
HandleHmpCommandCo *data = opaque;
- data->cmd->cmd(data->mon, data->qdict);
+ handle_hmp_command_exec(data->mon, data->cmd, data->qdict);
monitor_set_cur(qemu_coroutine_self(), NULL);
data->done = true;
}
return;
}
- if (!cmd->cmd) {
+ if (!cmd->cmd && !cmd->cmd_info_hrt) {
/* FIXME: is it useful to try autoload modules here ??? */
monitor_printf(&mon->common, "Command \"%.*s\" is not available.\n",
(int)(cmdline - cmd_start), cmd_start);
if (!cmd->coroutine) {
/* old_mon is non-NULL when called from qmp_human_monitor_command() */
Monitor *old_mon = monitor_set_cur(qemu_coroutine_self(), &mon->common);
- cmd->cmd(&mon->common, qdict);
+ handle_hmp_command_exec(&mon->common, cmd, qdict);
monitor_set_cur(qemu_coroutine_self(), old_mon);
} else {
HandleHmpCommandCo data = {
#include "qapi/qapi-commands-misc.h"
#include "qapi/qapi-commands-qom.h"
#include "qapi/qapi-commands-trace.h"
+#include "qapi/qapi-commands-machine.h"
#include "qapi/qapi-init-commands.h"
#include "qapi/error.h"
#include "qapi/qmp-event.h"
mtree_info(flatview, dispatch_tree, owner, disabled);
}
-#ifdef CONFIG_PROFILER
-
-int64_t dev_time;
-
-static void hmp_info_profile(Monitor *mon, const QDict *qdict)
-{
- static int64_t last_cpu_exec_time;
- int64_t cpu_exec_time;
- int64_t delta;
-
- cpu_exec_time = tcg_cpu_exec_time();
- delta = cpu_exec_time - last_cpu_exec_time;
-
- monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n",
- dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
- monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n",
- delta, delta / (double)NANOSECONDS_PER_SECOND);
- last_cpu_exec_time = cpu_exec_time;
- dev_time = 0;
-}
-#else
-static void hmp_info_profile(Monitor *mon, const QDict *qdict)
-{
- monitor_printf(mon, "Internal profiler not compiled\n");
-}
-#endif
-
/* Capture support */
static QLIST_HEAD (capture_list_head, CaptureState) capture_head;
while (table->name != NULL) {
if (strcmp(table->name, name) == 0) {
- g_assert(table->cmd == NULL);
+ g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL);
table->cmd = cmd;
return;
}
g_assert_not_reached();
}
+void monitor_register_hmp_info_hrt(const char *name,
+ HumanReadableText *(*handler)(Error **errp))
+{
+ HMPCommand *table = hmp_info_cmds;
+
+ while (table->name != NULL) {
+ if (strcmp(table->name, name) == 0) {
+ g_assert(table->cmd == NULL && table->cmd_info_hrt == NULL);
+ table->cmd_info_hrt = handler;
+ return;
+ }
+ table++;
+ }
+ g_assert_not_reached();
+}
+
void monitor_init_globals(void)
{
monitor_init_globals_core();
const char *help;
const char *flags; /* p=preconfig */
void (*cmd)(Monitor *mon, const QDict *qdict);
+ /*
+ * If implementing a command that takes no arguments and simply
+ * prints formatted data, then leave @cmd NULL, and then set
+ * @cmd_info_hrt to the corresponding QMP handler that returns
+ * the formatted text.
+ */
+ HumanReadableText *(*cmd_info_hrt)(Error **errp);
bool coroutine;
/*
* @sub_table is a list of 2nd level of commands. If it does not exist,
#include "qapi/qapi-commands-machine.h"
#include "qapi/qapi-commands-misc.h"
#include "qapi/qapi-commands-ui.h"
+#include "qapi/type-helpers.h"
#include "qapi/qmp/qerror.h"
+#include "exec/ramlist.h"
#include "hw/mem/memory-device.h"
#include "hw/acpi/acpi_dev_interface.h"
+#include "hw/intc/intc.h"
+#include "hw/rdma/rdma.h"
NameInfo *qmp_query_name(Error **errp)
{
abort();
}
}
+
+#ifdef CONFIG_PROFILER
+
+int64_t dev_time;
+
+HumanReadableText *qmp_x_query_profile(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+ static int64_t last_cpu_exec_time;
+ int64_t cpu_exec_time;
+ int64_t delta;
+
+ cpu_exec_time = tcg_cpu_exec_time();
+ delta = cpu_exec_time - last_cpu_exec_time;
+
+ g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n",
+ dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
+ g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n",
+ delta, delta / (double)NANOSECONDS_PER_SECOND);
+ last_cpu_exec_time = cpu_exec_time;
+ dev_time = 0;
+
+ return human_readable_text_from_str(buf);
+}
+#else
+HumanReadableText *qmp_x_query_profile(Error **errp)
+{
+ error_setg(errp, "Internal profiler not compiled");
+ return NULL;
+}
+#endif
+
+static int qmp_x_query_rdma_foreach(Object *obj, void *opaque)
+{
+ RdmaProvider *rdma;
+ RdmaProviderClass *k;
+ GString *buf = opaque;
+
+ if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) {
+ rdma = RDMA_PROVIDER(obj);
+ k = RDMA_PROVIDER_GET_CLASS(obj);
+ if (k->format_statistics) {
+ k->format_statistics(rdma, buf);
+ } else {
+ g_string_append_printf(buf,
+ "RDMA statistics not available for %s.\n",
+ object_get_typename(obj));
+ }
+ }
+
+ return 0;
+}
+
+HumanReadableText *qmp_x_query_rdma(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+
+ object_child_foreach_recursive(object_get_root(),
+ qmp_x_query_rdma_foreach, buf);
+
+ return human_readable_text_from_str(buf);
+}
+
+HumanReadableText *qmp_x_query_ramblock(Error **errp)
+{
+ g_autoptr(GString) buf = ram_block_format();
+
+ return human_readable_text_from_str(buf);
+}
+
+static int qmp_x_query_irq_foreach(Object *obj, void *opaque)
+{
+ InterruptStatsProvider *intc;
+ InterruptStatsProviderClass *k;
+ GString *buf = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) {
+ intc = INTERRUPT_STATS_PROVIDER(obj);
+ k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj);
+ uint64_t *irq_counts;
+ unsigned int nb_irqs, i;
+ if (k->get_statistics &&
+ k->get_statistics(intc, &irq_counts, &nb_irqs)) {
+ if (nb_irqs > 0) {
+ g_string_append_printf(buf, "IRQ statistics for %s:\n",
+ object_get_typename(obj));
+ for (i = 0; i < nb_irqs; i++) {
+ if (irq_counts[i] > 0) {
+ g_string_append_printf(buf, "%2d: %" PRId64 "\n", i,
+ irq_counts[i]);
+ }
+ }
+ }
+ } else {
+ g_string_append_printf(buf,
+ "IRQ statistics not available for %s.\n",
+ object_get_typename(obj));
+ }
+ }
+
+ return 0;
+}
+
+HumanReadableText *qmp_x_query_irq(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+
+ object_child_foreach_recursive(object_get_root(),
+ qmp_x_query_irq_foreach, buf);
+
+ return human_readable_text_from_str(buf);
+}
{ 'enum': 'GrabToggleKeys',
'data': [ 'ctrl-ctrl', 'alt-alt', 'shift-shift','meta-meta', 'scrolllock',
'ctrl-scrolllock' ] }
+
+##
+# @HumanReadableText:
+#
+# @human-readable-text: Formatted output intended for humans.
+#
+# Since: 6.2
+#
+##
+{ 'struct': 'HumanReadableText',
+ 'data': { 'human-readable-text': 'str' } }
'*cores': 'int',
'*threads': 'int',
'*maxcpus': 'int' } }
+
+##
+# @x-query-irq:
+#
+# Query interrupt statistics
+#
+# Returns: interrupt statistics
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-irq',
+ 'returns': 'HumanReadableText' }
+
+##
+# @x-query-jit:
+#
+# Query TCG compiler statistics
+#
+# Returns: TCG compiler statistics
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-jit',
+ 'returns': 'HumanReadableText',
+ 'if': 'CONFIG_TCG' }
+
+##
+# @x-query-numa:
+#
+# Query NUMA topology information
+#
+# Returns: topology information
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-numa',
+ 'returns': 'HumanReadableText' }
+
+##
+# @x-query-opcount:
+#
+# Query TCG opcode counters
+#
+# Returns: TCG opcode counters
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-opcount',
+ 'returns': 'HumanReadableText',
+ 'if': 'CONFIG_TCG' }
+
+##
+# @x-query-profile:
+#
+# Query TCG profiling information
+#
+# Returns: profile information
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-profile',
+ 'returns': 'HumanReadableText' }
+
+##
+# @x-query-ramblock:
+#
+# Query system ramblock information
+#
+# Returns: system ramblock information
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-ramblock',
+ 'returns': 'HumanReadableText' }
+
+##
+# @x-query-rdma:
+#
+# Query RDMA state
+#
+# Returns: RDMA state
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-rdma',
+ 'returns': 'HumanReadableText' }
+
+##
+# @x-query-roms:
+#
+# Query information on the registered ROMS
+#
+# Returns: registered ROMs
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-roms',
+ 'returns': 'HumanReadableText' }
+
+##
+# @x-query-usb:
+#
+# Query information on the USB devices
+#
+# Returns: USB device information
+#
+# Since: 6.2
+##
+{ 'command': 'x-query-usb',
+ 'returns': 'HumanReadableText' }
'string-input-visitor.c',
'string-output-visitor.c',
))
+if have_system
+ util_ss.add(files('qapi-type-helpers.c'))
+endif
if have_system or have_tools
util_ss.add(files(
'qmp-dispatch.c',
--- /dev/null
+/*
+ * QAPI common helper functions
+ *
+ * This file provides helper functions related to types defined
+ * in the QAPI schema.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qapi/type-helpers.h"
+
+HumanReadableText *human_readable_text_from_str(GString *str)
+{
+ HumanReadableText *ret = g_new0(HumanReadableText, 1);
+
+ ret->human_readable_text = g_steal_pointer(&str->str);
+
+ return ret;
+}
qemu_mutex_unlock(&ram_list.mutex);
}
-void ram_block_dump(Monitor *mon)
+GString *ram_block_format(void)
{
RAMBlock *block;
char *psize;
+ GString *buf = g_string_new("");
RCU_READ_LOCK_GUARD();
- monitor_printf(mon, "%24s %8s %18s %18s %18s\n",
- "Block Name", "PSize", "Offset", "Used", "Total");
+ g_string_append_printf(buf, "%24s %8s %18s %18s %18s\n",
+ "Block Name", "PSize", "Offset", "Used", "Total");
RAMBLOCK_FOREACH(block) {
psize = size_to_str(block->page_size);
- monitor_printf(mon, "%24s %8s 0x%016" PRIx64 " 0x%016" PRIx64
- " 0x%016" PRIx64 "\n", block->idstr, psize,
- (uint64_t)block->offset,
- (uint64_t)block->used_length,
- (uint64_t)block->max_length);
+ g_string_append_printf(buf, "%24s %8s 0x%016" PRIx64 " 0x%016" PRIx64
+ " 0x%016" PRIx64 "\n", block->idstr, psize,
+ (uint64_t)block->offset,
+ (uint64_t)block->used_length,
+ (uint64_t)block->max_length);
g_free(psize);
}
+
+ return buf;
}
#ifdef __linux__
#include "qemu/osdep.h"
#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qapi/qapi-commands-machine.h"
#include "sysemu/sysemu.h"
#include "monitor/monitor.h"
#include "hw/usb.h"
return NULL;
}
+HumanReadableText *qmp_x_query_usb(Error **errp)
+{
+ error_setg(errp, "Support for USB devices not built-in");
+ return NULL;
+}
+
void hmp_info_usb(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "Support for USB devices not built-in\n");
}
x86_cpu_dump_local_apic_state(cs, CPU_DUMP_FPU);
}
-
-void hmp_info_io_apic(Monitor *mon, const QDict *qdict)
-{
- monitor_printf(mon, "This command is obsolete and will be "
- "removed soon. Please use 'info pic' instead.\n");
-}
tcg_profile_snapshot(prof, false, true);
}
-void tcg_dump_op_count(void)
+void tcg_dump_op_count(GString *buf)
{
TCGProfile prof = {};
int i;
tcg_profile_snapshot_table(&prof);
for (i = 0; i < NB_OPS; i++) {
- qemu_printf("%s %" PRId64 "\n", tcg_op_defs[i].name,
- prof.table_op_count[i]);
+ g_string_append_printf(buf, "%s %" PRId64 "\n", tcg_op_defs[i].name,
+ prof.table_op_count[i]);
}
}
return ret;
}
#else
-void tcg_dump_op_count(void)
+void tcg_dump_op_count(GString *buf)
{
- qemu_printf("[TCG profiler not compiled]\n");
+ g_string_append_printf(buf, "[TCG profiler not compiled]\n");
}
int64_t tcg_cpu_exec_time(void)
}
#ifdef CONFIG_PROFILER
-void tcg_dump_info(void)
+void tcg_dump_info(GString *buf)
{
TCGProfile prof = {};
const TCGProfile *s;
tb_div_count = tb_count ? tb_count : 1;
tot = s->interm_time + s->code_time;
- qemu_printf("JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n",
- tot, tot / 2.4e9);
- qemu_printf("translated TBs %" PRId64 " (aborted=%" PRId64
- " %0.1f%%)\n",
- tb_count, s->tb_count1 - tb_count,
- (double)(s->tb_count1 - s->tb_count)
- / (s->tb_count1 ? s->tb_count1 : 1) * 100.0);
- qemu_printf("avg ops/TB %0.1f max=%d\n",
- (double)s->op_count / tb_div_count, s->op_count_max);
- qemu_printf("deleted ops/TB %0.2f\n",
- (double)s->del_op_count / tb_div_count);
- qemu_printf("avg temps/TB %0.2f max=%d\n",
- (double)s->temp_count / tb_div_count, s->temp_count_max);
- qemu_printf("avg host code/TB %0.1f\n",
- (double)s->code_out_len / tb_div_count);
- qemu_printf("avg search data/TB %0.1f\n",
- (double)s->search_out_len / tb_div_count);
+ g_string_append_printf(buf, "JIT cycles %" PRId64
+ " (%0.3f s at 2.4 GHz)\n",
+ tot, tot / 2.4e9);
+ g_string_append_printf(buf, "translated TBs %" PRId64
+ " (aborted=%" PRId64 " %0.1f%%)\n",
+ tb_count, s->tb_count1 - tb_count,
+ (double)(s->tb_count1 - s->tb_count)
+ / (s->tb_count1 ? s->tb_count1 : 1) * 100.0);
+ g_string_append_printf(buf, "avg ops/TB %0.1f max=%d\n",
+ (double)s->op_count / tb_div_count, s->op_count_max);
+ g_string_append_printf(buf, "deleted ops/TB %0.2f\n",
+ (double)s->del_op_count / tb_div_count);
+ g_string_append_printf(buf, "avg temps/TB %0.2f max=%d\n",
+ (double)s->temp_count / tb_div_count,
+ s->temp_count_max);
+ g_string_append_printf(buf, "avg host code/TB %0.1f\n",
+ (double)s->code_out_len / tb_div_count);
+ g_string_append_printf(buf, "avg search data/TB %0.1f\n",
+ (double)s->search_out_len / tb_div_count);
- qemu_printf("cycles/op %0.1f\n",
- s->op_count ? (double)tot / s->op_count : 0);
- qemu_printf("cycles/in byte %0.1f\n",
- s->code_in_len ? (double)tot / s->code_in_len : 0);
- qemu_printf("cycles/out byte %0.1f\n",
- s->code_out_len ? (double)tot / s->code_out_len : 0);
- qemu_printf("cycles/search byte %0.1f\n",
- s->search_out_len ? (double)tot / s->search_out_len : 0);
+ g_string_append_printf(buf, "cycles/op %0.1f\n",
+ s->op_count ? (double)tot / s->op_count : 0);
+ g_string_append_printf(buf, "cycles/in byte %0.1f\n",
+ s->code_in_len ? (double)tot / s->code_in_len : 0);
+ g_string_append_printf(buf, "cycles/out byte %0.1f\n",
+ s->code_out_len ? (double)tot / s->code_out_len : 0);
+ g_string_append_printf(buf, "cycles/search byte %0.1f\n",
+ s->search_out_len ?
+ (double)tot / s->search_out_len : 0);
if (tot == 0) {
tot = 1;
}
- qemu_printf(" gen_interm time %0.1f%%\n",
- (double)s->interm_time / tot * 100.0);
- qemu_printf(" gen_code time %0.1f%%\n",
- (double)s->code_time / tot * 100.0);
- qemu_printf("optim./code time %0.1f%%\n",
- (double)s->opt_time / (s->code_time ? s->code_time : 1)
- * 100.0);
- qemu_printf("liveness/code time %0.1f%%\n",
- (double)s->la_time / (s->code_time ? s->code_time : 1) * 100.0);
- qemu_printf("cpu_restore count %" PRId64 "\n",
- s->restore_count);
- qemu_printf(" avg cycles %0.1f\n",
- s->restore_count ? (double)s->restore_time / s->restore_count : 0);
+ g_string_append_printf(buf, " gen_interm time %0.1f%%\n",
+ (double)s->interm_time / tot * 100.0);
+ g_string_append_printf(buf, " gen_code time %0.1f%%\n",
+ (double)s->code_time / tot * 100.0);
+ g_string_append_printf(buf, "optim./code time %0.1f%%\n",
+ (double)s->opt_time / (s->code_time ?
+ s->code_time : 1)
+ * 100.0);
+ g_string_append_printf(buf, "liveness/code time %0.1f%%\n",
+ (double)s->la_time / (s->code_time ?
+ s->code_time : 1) * 100.0);
+ g_string_append_printf(buf, "cpu_restore count %" PRId64 "\n",
+ s->restore_count);
+ g_string_append_printf(buf, " avg cycles %0.1f\n",
+ s->restore_count ?
+ (double)s->restore_time / s->restore_count : 0);
}
#else
-void tcg_dump_info(void)
+void tcg_dump_info(GString *buf)
{
- qemu_printf("[TCG profiler not compiled]\n");
+ g_string_append_printf(buf, "[TCG profiler not compiled]\n");
}
#endif
{ "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE },
{ "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR },
{ "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR },
+#ifndef CONFIG_PROFILER
+ { "x-query-profile", ERROR_CLASS_GENERIC_ERROR },
+#endif
+ /* Only valid with a USB bus added */
+ { "x-query-usb", ERROR_CLASS_GENERIC_ERROR },
+ /* Only valid with accel=tcg */
+ { "x-query-jit", ERROR_CLASS_GENERIC_ERROR },
+ { "x-query-opcount", ERROR_CLASS_GENERIC_ERROR },
{ NULL, -1 }
};
int i;