@samitouri / QOSamiQemu / commits / e43c84813f

util: introduce some API docs for logging APIs

There is a gotcha with qemu_log() usage in a threaded process. If fragments of a log message are output via qemu_log() it is possible for messages from two threads to get mixed up. To prevent this qemu_log_trylock() should be used, along with fprintf(f) calls. This is a subtle problem that needs to be explained in the API docs to ensure correct usage. In the Rust code, the log_mask_ln method which is conceptually equivalent to the C qemu_log() call will unconditionally append a newline so must only ever be used for complete log messages. Reported-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>

Daniel P. Berrangé committed Sep 24, 2025 at 10:16 UTC e43c84813f96a496403a869973acd1a4278fec3c
3 files changed +54 -1
include/qemu/log-for-trace.h
+16 -1
@@ -29,7 +29,22 @@ static inline bool qemu_loglevel_mask(int mask)
29 return (qemu_loglevel & mask) != 0;
30 }
31
32 -/* main logging function */
32 +/**
33 + * qemu_log: report a log message
34 + * @fmt: the format string for the message
35 + * @...: the format string arguments
36 + *
37 + * This will emit a log message to the current output stream.
38 + *
39 + * The @fmt string should normally represent a complete line
40 + * of text, and thus end with a newline character.
41 + *
42 + * While it is possible to incrementally output fragments of
43 + * a complete line using qemu_log, this is inefficient and
44 + * races with other threads. For outputting fragments it is
45 + * strongly preferred to use the qemu_log_trylock() method
46 + * combined with fprintf().
47 + */
48 void G_GNUC_PRINTF(1, 2) qemu_log(const char *fmt, ...);
49
50 #endif
include/qemu/log.h
+32
@@ -41,7 +41,39 @@ bool qemu_log_separate(void);
41
42 /* Lock/unlock output. */
43
44 +/**
45 + * Acquires a lock on the current log output stream.
46 + * The returned FILE object should be used with the
47 + * fprintf() function to output the log message, and
48 + * then qemu_log_unlock() called to release the lock.
49 + *
50 + * The primary use case is to be able to incrementally
51 + * output fragments of a complete log message in an
52 + * efficient and race free manner.
53 + *
54 + * The simpler qemu_log() method should normally only
55 + * be used to output complete log messages, and not
56 + * within scope of a qemu_log_trylock() call.
57 + *
58 + * A typical usage pattern would be
59 + *
60 + * FILE *f = qemu_log_trylock()
61 + *
62 + * fprintf(f, "Something ");
63 + * fprintf(f, "Something ");
64 + * fprintf(f, "Something ");
65 + * fprintf(f, "The end\n");
66 + *
67 + * qemu_log_unlock(f);
68 + *
69 + * Returns: the current FILE if available, NULL on error
70 + */
71 FILE *qemu_log_trylock(void) G_GNUC_WARN_UNUSED_RESULT;
72 +
73 +/**
74 + * Releases the lock on the log output, previously
75 + * acquired by qemu_log_trylock().
76 + */
77 void qemu_log_unlock(FILE *fd);
78
79 /* Logging functions: */
rust/util/src/log.rs
+6
@@ -135,6 +135,12 @@ impl Drop for LogGuard {
135 /// error_address,
136 /// );
137 /// ```
138 +///
139 +/// The `log_mask_ln` macro must only be used for emitting complete
140 +/// log messages. Where it is required to incrementally output string
141 +/// fragments to construct a complete message, `LogGuard::new()` must
142 +/// be directly used in combination with `writeln()` to avoid output
143 +/// races with other QEMU threads.
144 #[macro_export]
145 macro_rules! log_mask_ln {
146 ($mask:expr, $fmt:tt $($args:tt)*) => {{