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)*) => {{