Add use of RCU for qemu_logfile.
This now allows changing the logfile while logging is active, and also solves the issue of a seg fault while changing the logfile. Any read access to the qemu_logfile handle will use the rcu_read_lock()/unlock() around the use of the handle. To fetch the handle we will use atomic_rcu_read(). We also in many cases do a check for validity of the logfile handle before using it to deal with the case where the file is closed and set to NULL. The cases where we write to the qemu_logfile will use atomic_rcu_set(). Writers will also use call_rcu() with a newly added qemu_logfile_free function for freeing/closing when readers have finished. Signed-off-by: Robert Foley <robert.foley@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20191118211528.3221-6-robert.foley@linaro.org>
This commit is contained in:
parent
fc59d2d870
commit
7606488c0e
|
@ -15,8 +15,15 @@
|
||||||
*/
|
*/
|
||||||
static inline void log_cpu_state(CPUState *cpu, int flags)
|
static inline void log_cpu_state(CPUState *cpu, int flags)
|
||||||
{
|
{
|
||||||
|
QemuLogFile *logfile;
|
||||||
|
|
||||||
if (qemu_log_enabled()) {
|
if (qemu_log_enabled()) {
|
||||||
cpu_dump_state(cpu, qemu_logfile, flags);
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
|
cpu_dump_state(cpu, logfile->fd, flags);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,19 +47,37 @@ static inline void log_cpu_state_mask(int mask, CPUState *cpu, int flags)
|
||||||
static inline void log_target_disas(CPUState *cpu, target_ulong start,
|
static inline void log_target_disas(CPUState *cpu, target_ulong start,
|
||||||
target_ulong len)
|
target_ulong len)
|
||||||
{
|
{
|
||||||
target_disas(qemu_logfile, cpu, start, len);
|
QemuLogFile *logfile;
|
||||||
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
|
target_disas(logfile->fd, cpu, start, len);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void log_disas(void *code, unsigned long size)
|
static inline void log_disas(void *code, unsigned long size)
|
||||||
{
|
{
|
||||||
disas(qemu_logfile, code, size);
|
QemuLogFile *logfile;
|
||||||
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
|
disas(logfile->fd, code, size);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_USER_ONLY)
|
#if defined(CONFIG_USER_ONLY)
|
||||||
/* page_dump() output to the log file: */
|
/* page_dump() output to the log file: */
|
||||||
static inline void log_page_dump(void)
|
static inline void log_page_dump(void)
|
||||||
{
|
{
|
||||||
page_dump(qemu_logfile);
|
QemuLogFile *logfile;
|
||||||
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
|
page_dump(logfile->fd);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,9 +3,16 @@
|
||||||
|
|
||||||
/* A small part of this API is split into its own header */
|
/* A small part of this API is split into its own header */
|
||||||
#include "qemu/log-for-trace.h"
|
#include "qemu/log-for-trace.h"
|
||||||
|
#include "qemu/rcu.h"
|
||||||
|
|
||||||
|
typedef struct QemuLogFile {
|
||||||
|
struct rcu_head rcu;
|
||||||
|
FILE *fd;
|
||||||
|
} QemuLogFile;
|
||||||
|
|
||||||
/* Private global variable, don't use */
|
/* Private global variable, don't use */
|
||||||
extern FILE *qemu_logfile;
|
extern QemuLogFile *qemu_logfile;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The new API:
|
* The new API:
|
||||||
|
@ -25,7 +32,16 @@ static inline bool qemu_log_enabled(void)
|
||||||
*/
|
*/
|
||||||
static inline bool qemu_log_separate(void)
|
static inline bool qemu_log_separate(void)
|
||||||
{
|
{
|
||||||
return qemu_logfile != NULL && qemu_logfile != stderr;
|
QemuLogFile *logfile;
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile && logfile->fd != stderr) {
|
||||||
|
res = true;
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CPU_LOG_TB_OUT_ASM (1 << 0)
|
#define CPU_LOG_TB_OUT_ASM (1 << 0)
|
||||||
|
@ -55,8 +71,15 @@ static inline bool qemu_log_separate(void)
|
||||||
|
|
||||||
static inline FILE *qemu_log_lock(void)
|
static inline FILE *qemu_log_lock(void)
|
||||||
{
|
{
|
||||||
qemu_flockfile(qemu_logfile);
|
QemuLogFile *logfile;
|
||||||
return logfile->fd;
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
|
qemu_flockfile(logfile->fd);
|
||||||
|
return logfile->fd;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void qemu_log_unlock(FILE *fd)
|
static inline void qemu_log_unlock(FILE *fd)
|
||||||
|
@ -64,6 +87,7 @@ static inline void qemu_log_unlock(FILE *fd)
|
||||||
if (fd) {
|
if (fd) {
|
||||||
qemu_funlockfile(fd);
|
qemu_funlockfile(fd);
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Logging functions: */
|
/* Logging functions: */
|
||||||
|
@ -73,9 +97,14 @@ static inline void qemu_log_unlock(FILE *fd)
|
||||||
static inline void GCC_FMT_ATTR(1, 0)
|
static inline void GCC_FMT_ATTR(1, 0)
|
||||||
qemu_log_vprintf(const char *fmt, va_list va)
|
qemu_log_vprintf(const char *fmt, va_list va)
|
||||||
{
|
{
|
||||||
if (qemu_logfile) {
|
QemuLogFile *logfile;
|
||||||
vfprintf(qemu_logfile, fmt, va);
|
|
||||||
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
|
vfprintf(logfile->fd, fmt, va);
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* log only if a bit is set on the current loglevel mask:
|
/* log only if a bit is set on the current loglevel mask:
|
||||||
|
|
12
tcg/tcg.c
12
tcg/tcg.c
|
@ -2114,9 +2114,17 @@ static void tcg_dump_ops(TCGContext *s, bool have_prefs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (have_prefs || op->life) {
|
if (have_prefs || op->life) {
|
||||||
for (; col < 40; ++col) {
|
|
||||||
putc(' ', qemu_logfile);
|
QemuLogFile *logfile;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
|
for (; col < 40; ++col) {
|
||||||
|
putc(' ', logfile->fd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op->life) {
|
if (op->life) {
|
||||||
|
|
72
util/log.c
72
util/log.c
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
static char *logfilename;
|
static char *logfilename;
|
||||||
static QemuMutex qemu_logfile_mutex;
|
static QemuMutex qemu_logfile_mutex;
|
||||||
FILE *qemu_logfile;
|
QemuLogFile *qemu_logfile;
|
||||||
int qemu_loglevel;
|
int qemu_loglevel;
|
||||||
static int log_append = 0;
|
static int log_append = 0;
|
||||||
static GArray *debug_regions;
|
static GArray *debug_regions;
|
||||||
|
@ -37,10 +37,14 @@ static GArray *debug_regions;
|
||||||
int qemu_log(const char *fmt, ...)
|
int qemu_log(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
if (qemu_logfile) {
|
QemuLogFile *logfile;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
ret = vfprintf(qemu_logfile, fmt, ap);
|
ret = vfprintf(logfile->fd, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
/* Don't pass back error results. */
|
/* Don't pass back error results. */
|
||||||
|
@ -48,6 +52,7 @@ int qemu_log(const char *fmt, ...)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,12 +61,24 @@ static void __attribute__((__constructor__)) qemu_logfile_init(void)
|
||||||
qemu_mutex_init(&qemu_logfile_mutex);
|
qemu_mutex_init(&qemu_logfile_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qemu_logfile_free(QemuLogFile *logfile)
|
||||||
|
{
|
||||||
|
g_assert(logfile);
|
||||||
|
|
||||||
|
if (logfile->fd != stderr) {
|
||||||
|
fclose(logfile->fd);
|
||||||
|
}
|
||||||
|
g_free(logfile);
|
||||||
|
}
|
||||||
|
|
||||||
static bool log_uses_own_buffers;
|
static bool log_uses_own_buffers;
|
||||||
|
|
||||||
/* enable or disable low levels log */
|
/* enable or disable low levels log */
|
||||||
void qemu_set_log(int log_flags)
|
void qemu_set_log(int log_flags)
|
||||||
{
|
{
|
||||||
bool need_to_open_file = false;
|
bool need_to_open_file = false;
|
||||||
|
QemuLogFile *logfile;
|
||||||
|
|
||||||
qemu_loglevel = log_flags;
|
qemu_loglevel = log_flags;
|
||||||
#ifdef CONFIG_TRACE_LOG
|
#ifdef CONFIG_TRACE_LOG
|
||||||
qemu_loglevel |= LOG_TRACE;
|
qemu_loglevel |= LOG_TRACE;
|
||||||
|
@ -79,43 +96,47 @@ void qemu_set_log(int log_flags)
|
||||||
}
|
}
|
||||||
qemu_mutex_lock(&qemu_logfile_mutex);
|
qemu_mutex_lock(&qemu_logfile_mutex);
|
||||||
if (qemu_logfile && !need_to_open_file) {
|
if (qemu_logfile && !need_to_open_file) {
|
||||||
qemu_mutex_unlock(&qemu_logfile_mutex);
|
logfile = qemu_logfile;
|
||||||
qemu_log_close();
|
atomic_rcu_set(&qemu_logfile, NULL);
|
||||||
|
call_rcu(logfile, qemu_logfile_free, rcu);
|
||||||
} else if (!qemu_logfile && need_to_open_file) {
|
} else if (!qemu_logfile && need_to_open_file) {
|
||||||
|
logfile = g_new0(QemuLogFile, 1);
|
||||||
if (logfilename) {
|
if (logfilename) {
|
||||||
qemu_logfile = fopen(logfilename, log_append ? "a" : "w");
|
logfile->fd = fopen(logfilename, log_append ? "a" : "w");
|
||||||
if (!qemu_logfile) {
|
if (!logfile->fd) {
|
||||||
|
g_free(logfile);
|
||||||
perror(logfilename);
|
perror(logfilename);
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
/* In case we are a daemon redirect stderr to logfile */
|
/* In case we are a daemon redirect stderr to logfile */
|
||||||
if (is_daemonized()) {
|
if (is_daemonized()) {
|
||||||
dup2(fileno(qemu_logfile), STDERR_FILENO);
|
dup2(fileno(logfile->fd), STDERR_FILENO);
|
||||||
fclose(qemu_logfile);
|
fclose(logfile->fd);
|
||||||
/* This will skip closing logfile in qemu_log_close() */
|
/* This will skip closing logfile in qemu_log_close() */
|
||||||
qemu_logfile = stderr;
|
logfile->fd = stderr;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Default to stderr if no log file specified */
|
/* Default to stderr if no log file specified */
|
||||||
assert(!is_daemonized());
|
assert(!is_daemonized());
|
||||||
qemu_logfile = stderr;
|
logfile->fd = stderr;
|
||||||
}
|
}
|
||||||
/* must avoid mmap() usage of glibc by setting a buffer "by hand" */
|
/* must avoid mmap() usage of glibc by setting a buffer "by hand" */
|
||||||
if (log_uses_own_buffers) {
|
if (log_uses_own_buffers) {
|
||||||
static char logfile_buf[4096];
|
static char logfile_buf[4096];
|
||||||
|
|
||||||
setvbuf(qemu_logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
|
setvbuf(logfile->fd, logfile_buf, _IOLBF, sizeof(logfile_buf));
|
||||||
} else {
|
} else {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
/* Win32 doesn't support line-buffering, so use unbuffered output. */
|
/* Win32 doesn't support line-buffering, so use unbuffered output. */
|
||||||
setvbuf(qemu_logfile, NULL, _IONBF, 0);
|
setvbuf(logfile->fd, NULL, _IONBF, 0);
|
||||||
#else
|
#else
|
||||||
setvbuf(qemu_logfile, NULL, _IOLBF, 0);
|
setvbuf(logfile->fd, NULL, _IOLBF, 0);
|
||||||
#endif
|
#endif
|
||||||
log_append = 1;
|
log_append = 1;
|
||||||
}
|
}
|
||||||
qemu_mutex_unlock(&qemu_logfile_mutex);
|
atomic_rcu_set(&qemu_logfile, logfile);
|
||||||
}
|
}
|
||||||
|
qemu_mutex_unlock(&qemu_logfile_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_log_needs_buffers(void)
|
void qemu_log_needs_buffers(void)
|
||||||
|
@ -244,18 +265,27 @@ out:
|
||||||
/* fflush() the log file */
|
/* fflush() the log file */
|
||||||
void qemu_log_flush(void)
|
void qemu_log_flush(void)
|
||||||
{
|
{
|
||||||
fflush(qemu_logfile);
|
QemuLogFile *logfile;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
logfile = atomic_rcu_read(&qemu_logfile);
|
||||||
|
if (logfile) {
|
||||||
|
fflush(logfile->fd);
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Close the log file */
|
/* Close the log file */
|
||||||
void qemu_log_close(void)
|
void qemu_log_close(void)
|
||||||
{
|
{
|
||||||
|
QemuLogFile *logfile;
|
||||||
|
|
||||||
qemu_mutex_lock(&qemu_logfile_mutex);
|
qemu_mutex_lock(&qemu_logfile_mutex);
|
||||||
if (qemu_logfile) {
|
logfile = qemu_logfile;
|
||||||
if (qemu_logfile != stderr) {
|
|
||||||
fclose(qemu_logfile);
|
if (logfile) {
|
||||||
}
|
atomic_rcu_set(&qemu_logfile, NULL);
|
||||||
qemu_logfile = NULL;
|
call_rcu(logfile, qemu_logfile_free, rcu);
|
||||||
}
|
}
|
||||||
qemu_mutex_unlock(&qemu_logfile_mutex);
|
qemu_mutex_unlock(&qemu_logfile_mutex);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue