chardev: add logtimestamp option

Add an option to inject timestamps into serial log file.
That simplifies debugging a lot, when you can simply compare
QEMU logs with guest console logs.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Acked-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <20260201173633.413934-4-vsementsov@yandex-team.ru>
This commit is contained in:
Vladimir Sementsov-Ogievskiy
2026-02-01 20:36:31 +03:00
committed by Marc-André Lureau
parent 2eed5472ec
commit b108dcbebc
3 changed files with 65 additions and 6 deletions

View File

@@ -82,17 +82,62 @@ void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
CHARDEV_GET_CLASS(s)->chr_be_event(s, event);
}
static void do_write_log(Chardev *s, const uint8_t *buf, size_t len)
{
if (qemu_write_full(s->logfd, buf, len) < len) {
/*
* qemu_write_full() is defined with G_GNUC_WARN_UNUSED_RESULT,
* but logging is besteffort, we do ignore errors.
*/
}
}
static void do_write_log_timestamps(Chardev *s, const uint8_t *buf, size_t len)
{
g_autofree char *timestr = NULL;
while (len) {
size_t i;
if (s->log_line_start) {
if (!timestr) {
timestr = real_time_iso8601();
}
do_write_log(s, (const uint8_t *)timestr, strlen(timestr));
do_write_log(s, (const uint8_t *)" ", 1);
s->log_line_start = false;
}
for (i = 0; i < len; i++) {
if (buf[i] == '\n') {
break;
}
}
if (i == len) {
/* not found \n */
do_write_log(s, buf, len);
return;
}
i += 1;
do_write_log(s, buf, i);
buf += i;
len -= i;
s->log_line_start = true;
}
}
static void qemu_chr_write_log(Chardev *s, const uint8_t *buf, size_t len)
{
if (s->logfd < 0) {
return;
}
if (qemu_write_full(s->logfd, buf, len) < len) {
/*
* qemu_write_full() is defined with G_GNUC_WARN_UNUSED_RESULT,
* but logging is besteffort, we do ignore errors.
*/
if (s->logtimestamp) {
do_write_log_timestamps(s, buf, len);
} else {
do_write_log(s, buf, len);
}
}
@@ -248,6 +293,7 @@ static bool qemu_char_open(Chardev *chr, ChardevBackend *backend, Error **errp)
} else {
flags |= O_TRUNC;
}
chr->logtimestamp = common->has_logtimestamp && common->logtimestamp;
chr->logfd = qemu_create(common->logfile, flags, 0666, errp);
if (chr->logfd < 0) {
return false;
@@ -267,6 +313,7 @@ static void char_init(Object *obj)
chr->handover_yank_instance = false;
chr->logfd = -1;
chr->log_line_start = true;
qemu_mutex_init(&chr->chr_write_lock);
/*
@@ -505,6 +552,9 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend)
backend->logfile = g_strdup(logfile);
backend->has_logappend = true;
backend->logappend = qemu_opt_get_bool(opts, "logappend", false);
backend->has_logtimestamp = true;
backend->logtimestamp = qemu_opt_get_bool(opts, "logtimestamp", false);
}
static const ChardevClass *char_get_class(const char *driver, Error **errp)
@@ -956,6 +1006,9 @@ QemuOptsList qemu_chardev_opts = {
},{
.name = "logappend",
.type = QEMU_OPT_BOOL,
},{
.name = "logtimestamp",
.type = QEMU_OPT_BOOL,
},{
.name = "mouse",
.type = QEMU_OPT_BOOL,

View File

@@ -63,6 +63,8 @@ struct Chardev {
CharFrontend *fe;
char *label;
int logfd;
bool logtimestamp;
bool log_line_start;
int be_open;
/* used to coordinate the chardev-change special-case: */
bool handover_yank_instance;

View File

@@ -197,11 +197,15 @@
# @logappend: true to append instead of truncate (default to false to
# truncate)
#
# @logtimestamp: true to insert timestamps into logfile
# (default false) (since 11.0)
#
# Since: 2.6
##
{ 'struct': 'ChardevCommon',
'data': { '*logfile': 'str',
'*logappend': 'bool' } }
'*logappend': 'bool',
'*logtimestamp': 'bool' } }
##
# @ChardevFile: