From: Markus Armbruster <armbru@redhat.com>
To: "Daniel P. Berrangé" <berrange@redhat.com>
Cc: qemu-devel@nongnu.org, qemu-block@nongnu.org,
"Hanna Reitz" <hreitz@redhat.com>,
"Kevin Wolf" <kwolf@redhat.com>,
"Marc-André Lureau" <marcandre.lureau@redhat.com>,
"Christian Schoenebeck" <qemu_oss@crudebyte.com>,
"Richard Henderson" <richard.henderson@linaro.org>,
"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
"Stefan Weil" <sw@weilnetz.de>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Gerd Hoffmann" <kraxel@redhat.com>,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Dr. David Alan Gilbert" <dave@treblig.org>
Subject: Re: [PATCH v3 14/20] util: fix interleaving of error & trace output
Date: Mon, 22 Sep 2025 14:16:35 +0200 [thread overview]
Message-ID: <87v7lak7h8.fsf@pond.sub.org> (raw)
In-Reply-To: <20250910180357.320297-15-berrange@redhat.com> ("Daniel P. Berrangé"'s message of "Wed, 10 Sep 2025 19:03:51 +0100")
Daniel P. Berrangé <berrange@redhat.com> writes:
> The monitor_cur_hmp() function will acquire/release mutex locks, which
> will trigger trace probes, which can in turn trigger qemu_log() calls.
> vreport() calls monitor_cur() multiple times through its execution
> both directly and indirectly via error_vprintf().
This issue goes back to commit e69ee454b5f9 (monitor: Make current
monitor a per-coroutine property). I've always disliked the hash table
(which is why we need to take the mutex), and now I dislike it even
more. See also my review of PATCH 02.
I'd love to have a lockless monitor_cur() again. But I don't have one
up my sleeve, so we need to work around the issue somehow.
> The result is that the prefix information printed by vreport() gets
> interleaved with qemu_log() output, when run outside the context of
> an HMP command dispatcher. This can be seen with:
[...]
> To avoid this interleaving (as well as reduce the huge number of
> mutex lock/unlock calls) we need to ensure that monitor_cur_hmp() is
> only called once at the start of vreport(), and if no HMP is present,
> no further monitor APIs can be called.
>
> This implies error_[v]printf() cannot be called from vreport().
>
> Instead a decision about whether output will go to monitor_[v]printf()
> or fprintf() must be made upfront, and then those functions called
> directly.
>
> $ qemu-system-x86_64
> -msg timestamp=on,guest-name=on
> -display none
> -object tls-creds-x509,id=f,dir=fish
> -name fish
> -d trace:qemu_mutex*
> 2025-09-10T16:31:22.701691Z qemu_mutex_unlock released mutex 0x5626fd3b84c0 (/var/home/berrange/src/virt/qemu/include/qemu/lockable.h:56)
> 2025-09-10T16:31:22.701728Z qemu_mutex_lock waiting on mutex 0x5626fd3b53e0 (/var/home/berrange/src/virt/qemu/include/qemu/lockable.h:56)
> 2025-09-10T16:31:22.701730Z qemu_mutex_locked taken mutex 0x5626fd3b53e0 (/var/home/berrange/src/virt/qemu/include/qemu/lockable.h:56)
> 2025-09-10T16:31:22.701732Z qemu_mutex_unlock released mutex 0x5626fd3b53e0 (/var/home/berrange/src/virt/qemu/include/qemu/lockable.h:56)
> 2025-09-10T16:31:22.703989Z qemu_mutex_lock waiting on mutex 0x5626fd3b5560 (../monitor/monitor.c:91)
> 2025-09-10T16:31:22.703996Z qemu_mutex_locked taken mutex 0x5626fd3b5560 (../monitor/monitor.c:91)
> 2025-09-10T16:31:22.703999Z qemu_mutex_unlock released mutex 0x5626fd3b5560 (../monitor/monitor.c:96)
> 2025-09-10T16:31:22.704000Z fish qemu-system-x86_64: Unable to access credentials fish/ca-cert.pem: No such file or directory
>
> This change requires adding a stub for monitor_printf() since that
> is now called directly by vreport(), where monitor_vprintf() was
> only needed previously.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> stubs/monitor-core.c | 5 +++
> tests/unit/test-util-sockets.c | 1 +
> util/error-report.c | 60 ++++++++++++++++++++++++----------
> 3 files changed, 48 insertions(+), 18 deletions(-)
>
> diff --git a/stubs/monitor-core.c b/stubs/monitor-core.c
> index 1e0b11ec29..70b1f7a994 100644
> --- a/stubs/monitor-core.c
> +++ b/stubs/monitor-core.c
> @@ -30,3 +30,8 @@ int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
> {
> abort();
> }
> +
> +int monitor_printf(Monitor *mon, const char *fmt, ...)
> +{
> + abort();
> +}
> diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c
> index d40813c682..53733163dd 100644
> --- a/tests/unit/test-util-sockets.c
> +++ b/tests/unit/test-util-sockets.c
> @@ -76,6 +76,7 @@ Monitor *monitor_cur_hmp(void) { return cur_mon; }
> bool monitor_cur_is_qmp(void) { return false; }
> Monitor *monitor_set_cur(Coroutine *co, Monitor *mon) { abort(); }
> int monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { abort(); }
> +int monitor_printf(Monitor *mon, const char *fmt, ...) { abort(); }
>
> #ifndef _WIN32
> static void test_socket_fd_pass_name_good(void)
> diff --git a/util/error-report.c b/util/error-report.c
> index c0fcf388de..6c887047e3 100644
> --- a/util/error-report.c
> +++ b/util/error-report.c
> @@ -29,6 +29,9 @@ bool message_with_timestamp;
> bool error_with_guestname;
> const char *error_guest_name;
>
> +typedef void (*error_print_func)(void *opaque, const char *fmt, ...);
> +typedef void (*error_print_vfunc)(void *opaque, const char *fmt, va_list ap);
> +
> /*
> * Print to current monitor if we have one, else to stderr.
> */
> @@ -151,34 +154,34 @@ void loc_set_file(const char *fname, int lno)
> /*
> * Print current location to current monitor if we have one, else to stderr.
> */
> -static void print_loc(void)
> +static void print_loc(bool cur, error_print_func print_func, void *print_opaque)
> {
> const char *sep = "";
> int i;
> const char *const *argp;
>
> - if (!monitor_cur_hmp() && g_get_prgname()) {
> - error_printf("%s:", g_get_prgname());
> + if (!cur && g_get_prgname()) {
> + print_func(print_opaque, "%s:", g_get_prgname());
> sep = " ";
> }
> switch (cur_loc->kind) {
> case LOC_CMDLINE:
> argp = cur_loc->ptr;
> for (i = 0; i < cur_loc->num; i++) {
> - error_printf("%s%s", sep, argp[i]);
> + print_func(print_opaque, "%s%s", sep, argp[i]);
> sep = " ";
> }
> - error_printf(": ");
> + print_func(print_opaque, ": ");
> break;
> case LOC_FILE:
> - error_printf("%s:", (const char *)cur_loc->ptr);
> + print_func(print_opaque, "%s:", (const char *)cur_loc->ptr);
> if (cur_loc->num) {
> - error_printf("%d:", cur_loc->num);
> + print_func(print_opaque, "%d:", cur_loc->num);
> }
> - error_printf(" ");
> + print_func(print_opaque, " ");
> break;
> default:
> - error_printf("%s", sep);
> + print_func(print_opaque, "%s", sep);
> }
> }
>
> @@ -199,34 +202,55 @@ real_time_iso8601(void)
> G_GNUC_PRINTF(2, 0)
> static void vreport(report_type type, const char *fmt, va_list ap)
> {
> + /*
> + * Calling monitor_cur_hmp() will acquire/release mutexes,
> + * which triggers trace probes, which can trigger
> + * qemu_log calls, which would interleave with output
> + * from this. Hence cache the monitor handle upfront
> + * so any tracing appears before we start outputting.
> + */
> + Monitor *cur = monitor_cur_hmp();
> + error_print_func print_func;
> + error_print_vfunc print_vfunc;
> + void *print_opaque;
> gchar *timestr;
>
> - if (message_with_timestamp && !monitor_cur_hmp()) {
> + if (cur) {
> + print_func = (error_print_func)monitor_printf;
> + print_vfunc = (error_print_vfunc)monitor_vprintf;
> + print_opaque = cur;
> + } else {
> + print_func = (error_print_func)fprintf;
> + print_vfunc = (error_print_vfunc)vfprintf;
> + print_opaque = stderr;
> + }
print_func is is void (*)(void *, const char *, ...).
monitor_printf() is int (*)(Monitor *, const char *, ...).
fprintf() is int (*)(FILE *, const char *, ...)
ISO/IEC 9899:1999 §6.3.2.3 Pointers:
[#8] A pointer to a function of one type may be converted to
a pointer to a function of another type and back again; the
result shall compare equal to the original pointer. If a
converted pointer is used to call a function whose type is
not compatible with the pointed-to type, the behavior is
undefined.
For this to work in practice, three conditions need to hold:
1. Both Monitor * and FILE * arguments are passed exactly like void *
arguments. Holds on sane machines.
2. A function returning void is called the same way as a function
returning int when the value is ignored. Holds on sane machines. We
can easily eliminate this condition: just change print_func from void to
int.
3. The guys writing the compiler refrain from making use of their
self-granted license to ruin our day because our program has undefined
behavior. Since improving some SPEC benchmark by half a percent is
clearly worth breaking any number of working programs, ...
If we want to stay out of this swamp, we need to define wrapper
functions taking void * and returning it.
> +
> + if (message_with_timestamp && !cur) {
> timestr = real_time_iso8601();
> - error_printf("%s ", timestr);
> + print_func(print_opaque, "%s ", timestr);
> g_free(timestr);
> }
>
> /* Only prepend guest name if -msg guest-name and -name guest=... are set */
> - if (error_with_guestname && error_guest_name && !monitor_cur_hmp()) {
> - error_printf("%s ", error_guest_name);
> + if (error_with_guestname && error_guest_name && !cur) {
> + print_func(print_opaque, "%s ", error_guest_name);
> }
>
> - print_loc();
> + print_loc(!!cur, print_func, print_opaque);
>
> switch (type) {
> case REPORT_TYPE_ERROR:
> break;
> case REPORT_TYPE_WARNING:
> - error_printf("warning: ");
> + print_func(print_opaque, "warning: ");
> break;
> case REPORT_TYPE_INFO:
> - error_printf("info: ");
> + print_func(print_opaque, "info: ");
> break;
> }
>
> - error_vprintf(fmt, ap);
> - error_printf("\n");
> + print_vfunc(print_opaque, fmt, ap);
> + print_func(print_opaque, "\n");
> }
>
> /*
The code becomes more complicated. Hmm.
As long as monitor_cur() hits trace points, we really want to call it
just once, before we start printing the error message.
Calls during vreport() (in master, because I can't be bothered to
recompile):
(1) At the beginning of vreport(). Fine.
(2) In the middle of vreport(). Easy to avoid: reuse the value gotten
at (1).
(3) In print_loc(). Easy to avoid: called only by vreport(), so pass
the value gotten at (1).
(4) In error_vprintf(), called multiple times from vreport() and
print_loc() directly and via error_printf(). Obvious, stupid solution:
have error_vprintf() and error_printf() take a Monitor *argument. But
that's no good, because we have numerous callers, and we'd rather not
change these to pass monitor_cur(). So create two new functions, and
make the existing ones wrap around them.
Thoughts?
next prev parent reply other threads:[~2025-09-22 12:17 UTC|newest]
Thread overview: 84+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-10 18:03 [PATCH v3 00/20] util: sync error_report & qemu_log output more closely Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 01/20] include: define constant for early constructor priority Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 02/20] monitor: initialize global data from a constructor Daniel P. Berrangé
2025-09-17 14:07 ` Markus Armbruster
2025-09-17 15:31 ` Daniel P. Berrangé
2025-09-18 6:30 ` Markus Armbruster
2025-09-10 18:03 ` [PATCH v3 03/20] system: unconditionally enable thread naming Daniel P. Berrangé
2025-09-11 12:18 ` Ján Tomko
2025-09-19 8:20 ` Markus Armbruster
2025-09-19 13:32 ` Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 04/20] util: expose qemu_thread_set_name Daniel P. Berrangé
2025-09-19 8:39 ` Markus Armbruster
2025-09-24 8:37 ` Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 05/20] audio: make jackaudio use qemu_thread_set_name Daniel P. Berrangé
2025-09-11 13:21 ` Christian Schoenebeck
2025-09-12 14:58 ` Daniel P. Berrangé
2025-09-19 8:46 ` Markus Armbruster
2025-09-10 18:03 ` [PATCH v3 06/20] util: set the name for the 'main' thread Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 07/20] util: add API to fetch the current thread name Daniel P. Berrangé
2025-09-19 8:59 ` Markus Armbruster
2025-09-19 9:05 ` Daniel P. Berrangé
2025-09-19 11:35 ` Markus Armbruster
2025-09-23 12:12 ` Markus Armbruster
2025-09-24 8:55 ` Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 08/20] log: avoid repeated prefix on incremental qemu_log calls Daniel P. Berrangé
2025-09-10 23:20 ` Dr. David Alan Gilbert
2025-09-11 8:49 ` Richard Henderson
2025-09-17 14:11 ` Markus Armbruster
2025-09-17 15:33 ` Daniel P. Berrangé
2025-09-23 12:46 ` Philippe Mathieu-Daudé
2025-09-23 14:28 ` Markus Armbruster
2025-09-23 14:55 ` Daniel P. Berrangé
2025-09-24 7:06 ` Markus Armbruster
2025-09-24 7:12 ` Daniel P. Berrangé
2025-09-24 9:20 ` Daniel P. Berrangé
2025-09-24 12:34 ` Markus Armbruster
2025-09-24 13:30 ` Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 09/20] ui/vnc: remove use of error_printf_unless_qmp() Daniel P. Berrangé
2025-09-11 0:08 ` Dr. David Alan Gilbert
2025-09-11 8:51 ` Richard Henderson
2025-09-11 17:54 ` Richard Henderson
2025-09-12 15:11 ` Daniel P. Berrangé
2025-09-19 11:42 ` Markus Armbruster
2025-09-10 18:03 ` [PATCH v3 10/20] monitor: remove redundant error_[v]printf_unless_qmp Daniel P. Berrangé
2025-09-11 8:50 ` Richard Henderson
2025-09-19 11:42 ` Markus Armbruster
2025-09-23 12:48 ` Philippe Mathieu-Daudé
2025-09-10 18:03 ` [PATCH v3 11/20] monitor: move error_vprintf() back to error-report.c Daniel P. Berrangé
2025-09-11 8:55 ` Richard Henderson
2025-09-19 12:27 ` Markus Armbruster
2025-09-19 13:43 ` Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 12/20] monitor: introduce monitor_cur_hmp() function Daniel P. Berrangé
2025-09-11 17:52 ` Richard Henderson
2025-09-19 12:43 ` Markus Armbruster
2025-09-19 13:29 ` Daniel P. Berrangé
2025-09-20 7:06 ` Markus Armbruster
2025-09-20 11:54 ` Dr. David Alan Gilbert
2025-09-22 8:38 ` Markus Armbruster
2025-09-24 16:09 ` Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 13/20] util: don't skip error prefixes when QMP is active Daniel P. Berrangé
2025-09-22 8:43 ` Markus Armbruster
2025-09-10 18:03 ` [PATCH v3 14/20] util: fix interleaving of error & trace output Daniel P. Berrangé
2025-09-11 18:01 ` Richard Henderson
2025-09-11 18:05 ` Richard Henderson
2025-09-12 16:30 ` Daniel P. Berrangé
2025-09-22 12:16 ` Markus Armbruster [this message]
2025-09-10 18:03 ` [PATCH v3 15/20] util: introduce common helper for error-report & log code Daniel P. Berrangé
2025-09-11 18:02 ` Richard Henderson
2025-09-10 18:03 ` [PATCH v3 16/20] util: convert error-report & log to message API for timestamp Daniel P. Berrangé
2025-09-11 18:04 ` Richard Henderson
2025-09-10 18:03 ` [PATCH v3 17/20] util: add support for formatting a workload name in messages Daniel P. Berrangé
2025-09-11 18:06 ` Richard Henderson
2025-09-11 20:27 ` Dr. David Alan Gilbert
2025-09-12 8:34 ` Daniel P. Berrangé
2025-09-12 11:18 ` Dr. David Alan Gilbert
2025-09-10 18:03 ` [PATCH v3 18/20] util: add support for formatting a program " Daniel P. Berrangé
2025-09-11 18:07 ` Richard Henderson
2025-09-11 18:08 ` Richard Henderson
2025-09-12 16:33 ` Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 19/20] util: add support for formatting thread info " Daniel P. Berrangé
2025-09-11 18:11 ` Richard Henderson
2025-09-12 8:36 ` Daniel P. Berrangé
2025-09-10 18:03 ` [PATCH v3 20/20] util: add brackets around guest name in message context Daniel P. Berrangé
2025-09-11 18:12 ` Richard Henderson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87v7lak7h8.fsf@pond.sub.org \
--to=armbru@redhat.com \
--cc=berrange@redhat.com \
--cc=dave@treblig.org \
--cc=hreitz@redhat.com \
--cc=kraxel@redhat.com \
--cc=kwolf@redhat.com \
--cc=manos.pitsidianakis@linaro.org \
--cc=marcandre.lureau@redhat.com \
--cc=pbonzini@redhat.com \
--cc=philmd@linaro.org \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=qemu_oss@crudebyte.com \
--cc=richard.henderson@linaro.org \
--cc=sw@weilnetz.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.