From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Markus Armbruster <armbru@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 08/20] log: avoid repeated prefix on incremental qemu_log calls
Date: Wed, 24 Sep 2025 08:12:42 +0100 [thread overview]
Message-ID: <aNOZ6tSuxFolxs3N@redhat.com> (raw)
In-Reply-To: <87h5ws5nxs.fsf@pond.sub.org>
On Wed, Sep 24, 2025 at 09:06:55AM +0200, Markus Armbruster wrote:
> Daniel P. Berrangé <berrange@redhat.com> writes:
>
> > On Tue, Sep 23, 2025 at 04:28:42PM +0200, Markus Armbruster wrote:
> >> Daniel P. Berrangé <berrange@redhat.com> writes:
> >>
> >> > Some code makes multiple qemu_log calls to incrementally emit
> >> > a single message. Currently timestamps get prepended to all
> >> > qemu_log calls, even those continuing a previous incomplete
> >> > message.
> >> >
> >> > This changes the qemu_log so it skips adding a new line prefix,
> >> > if the previous qemu_log call did NOT end with a newline.
> >> >
> >> > Reported-by: Richard Henderson <richard.henderson@linaro.org>
> >> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> >>
> >> This patch has kept nagging me in the back of my brain. So I'm back for
> >> a second look.
> >>
> >> > ---
> >> > util/log.c | 9 ++++++++-
> >> > 1 file changed, 8 insertions(+), 1 deletion(-)
> >> >
> >> > diff --git a/util/log.c b/util/log.c
> >> > index abdcb6b311..2642a55c59 100644
> >> > --- a/util/log.c
> >> > +++ b/util/log.c
> >> > @@ -143,6 +143,12 @@ void qemu_log_unlock(FILE *logfile)
> >> > }
> >> > }
> >> >
> >> > +/*
> >> > + * 'true' if the previous log message lacked a trailing '\n',
> >> > + * and thus the subsequent call must skip any prefix
> >> > + */
> >> > +static __thread bool incomplete;
> >> > +
> >> > void qemu_log(const char *fmt, ...)
> >> > {
> >> > FILE *f;
> >> > @@ -154,7 +160,7 @@ void qemu_log(const char *fmt, ...)
> >> > * was emitted if we are delayed acquiring the
> >> > * mutex
> >> > */
> >> > - if (message_with_timestamp) {
> >> > + if (message_with_timestamp && !incomplete) {
> >> > g_autoptr(GDateTime) dt = g_date_time_new_now_utc();
> >> > timestr = g_date_time_format_iso8601(dt);
> >> > }
> >> > @@ -170,6 +176,7 @@ void qemu_log(const char *fmt, ...)
> >> f = qemu_log_trylock();
> >> if (f) {
> >> va_list ap;
> >>
> >> if (timestr) {
> >> fprintf(f, "%s ", timestr);
> >> }
> >>
> >> > va_start(ap, fmt);
> >> > vfprintf(f, fmt, ap);
> >> > va_end(ap);
> >> > + incomplete = fmt[strlen(fmt) - 1] != '\n';
> >> > qemu_log_unlock(f);
> >> > }
> >> > }
> >>
> >> Two cases:
> >>
> >> (A) Single log
> >>
> >> qemu_log_trylock() returns @global_file, and uses RCU to ensure it
> >> remains valid until qemu_log_unlock(). I think.
> >>
> >> (B) Log split per thread (-d tid)
> >>
> >> qemu_log_trylock() returns thread-local @thread_file.
> >>
> >> In addition, qemu_log_trylock() locks the FILE it returns with
> >> flockfile(), so no other thread can write to it until qemu_log_unlock()
> >> unlocks it with funlockfile(). This ensures the entire output of in
> >> between stays together.
> >>
> >> Let's see how this plays with @incomplete.
> >>
> >> (B) Log split per thread (-d tid)
> >>
> >> @incomplete is thread-local. It records wether the last qemu_log()
> >> in this thread was an incomplete line. If it was, the next
> >> qemu_log() continues the line. Unless something else wrote to
> >> @thread_file in between, but that's not supposed to happen. Good.
> >>
> >> (A) Single log
> >>
> >> All thread log to the same FILE. Consider:
> >>
> >> 1. Thread 1 starts. Its @incomplete is initialized to false.
> >>
> >> 2. Thread 2 starts. Its @incomplete is initialized to false.
> >>
> >> 3. Thread 1 logs "abra". Its @incomplete is set to true.
> >>
> >> 4. Thread 2 logs "interrupt\n". Its @incomplete remains false.
> >>
> >> 5. Thread 2 logs "cadbra\n". Its @incomplete goes back to false.
> >>
> >> Resulting log file contents:
> >>
> >> PREFIX "abra" PREFIX "interrupt\n"
> >> "cadabra\n"
> >>
> >> Not good.
> >>
> >> We could complicate this code further to mitigate. For instance, we
> >> could use a thread-local @incomplete for (B), and a global one for (A).
> >> This ensures log lines start with PREFIX as they should, but does
> >> nothing to avoid mixing up line parts from different threads. My
> >> example would then produce
> >>
> >> PREFIX "abrainterrupt\n"
> >> PREFIX "cababra\n"
> >>
> >> My take: "Doctor, it hurts when I do that!" "Don't do that then."
> >> Logging incomplete lines with qemu_log() can hurt. Don't do that then.
> >
> > I just took a look at linux-user/syscall.c as that is one place that
> > heavily uses qemu_log() for incomplete lines.
> >
> > What I didn't realize was that the expectation is to call
> > qemu_log_trylock() (which returns the "FILE *" target) and
> > then you can ignore the "FILE *" and just call qemu_log()
> > repeatedly, and finally call qemu_log_unlock(FILE *) when
> > done.
> >
> > https://gitlab.com/qemu-project/qemu/-/blob/master/linux-user/strace.c?ref_type=heads#L4396
>
> I can see the qemu_log_trylock() / qemu_log_unlock() bracket. But the
> code within doesn't work the way you describe: it uses fprintf(f, ...).
> If it did ignore @f and call qemu_log(), qemu_log() would
> qemu_log_trylock() again, taking the RCU read lock and the flockfile()
> lock on @f recursively. Should work.
Only the fallback path (the 'else' branch) uses fprintf.
The main path is doing
scnames[i].call(cpu_env, &scnames[i], arg1, arg2, arg3,
arg4, arg5, arg6);
which calls out to the other countless 'print_XXXX' methods,
one per syscall, which all use qemu_log().
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
next prev parent reply other threads:[~2025-09-24 7:14 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é [this message]
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
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=aNOZ6tSuxFolxs3N@redhat.com \
--to=berrange@redhat.com \
--cc=armbru@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.