All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: Richard Henderson <richard.henderson@linaro.org>
Cc: qemu-devel@nongnu.org
Subject: Re: [PATCH v2 39/39] util/log: Support per-thread log files
Date: Thu, 14 Apr 2022 16:35:02 +0100	[thread overview]
Message-ID: <87y207smjt.fsf@linaro.org> (raw)
In-Reply-To: <20220326132534.543738-48-richard.henderson@linaro.org>


Richard Henderson <richard.henderson@linaro.org> writes:

> Add a new log flag, tid, to turn this feature on.
> Require the log filename to be set, and to contain %d.
>
> Do not allow tid to be turned off once it is on, nor let
> the filename be change thereafter.  This avoids the need
> for signalling each thread to re-open on a name change.
>
> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
> ---
> v2: Make use of CONFIG_GETTID, and fallback to SYS_gettid.
> ---
>  include/qemu/log.h |   1 +
>  util/log.c         | 149 ++++++++++++++++++++++++++++++++++-----------
>  2 files changed, 115 insertions(+), 35 deletions(-)
>
> diff --git a/include/qemu/log.h b/include/qemu/log.h
> index a325bca661..c5643d8dd5 100644
> --- a/include/qemu/log.h
> +++ b/include/qemu/log.h
> @@ -34,6 +34,7 @@ bool qemu_log_separate(void);
>  #define CPU_LOG_PLUGIN     (1 << 18)
>  /* LOG_STRACE is used for user-mode strace logging. */
>  #define LOG_STRACE         (1 << 19)
> +#define LOG_PER_THREAD     (1 << 20)
>  
>  /* Lock/unlock output. */
>  
> diff --git a/util/log.c b/util/log.c
> index df0710720f..0bb2233788 100644
> --- a/util/log.c
> +++ b/util/log.c
> @@ -27,6 +27,9 @@
>  #include "qemu/thread.h"
>  #include "qemu/lockable.h"
>  #include "qemu/rcu.h"
> +#ifdef CONFIG_LINUX
> +#include <sys/syscall.h>
> +#endif
>  
>  
>  typedef struct RCUCloseFILE {
> @@ -38,22 +41,36 @@ typedef struct RCUCloseFILE {
>  static QemuMutex global_mutex;
>  static char *global_filename;
>  static FILE *global_file;
> +static __thread FILE *thread_file;
>  
>  int qemu_loglevel;
> -static int log_append = 0;
> +static bool log_append;
> +static bool log_per_thread;
>  static GArray *debug_regions;
>  
>  /* Returns true if qemu_log() will really write somewhere. */
>  bool qemu_log_enabled(void)
>  {
> -    return qatomic_read(&global_file) != NULL;
> +    return log_per_thread || qatomic_read(&global_file) != NULL;
>  }
>  
>  /* Returns true if qemu_log() will write somewhere other than stderr. */
>  bool qemu_log_separate(void)
>  {
>      FILE *logfile = qatomic_read(&global_file);
> -    return logfile && logfile != stderr;
> +    return log_per_thread || (logfile && logfile != stderr);
> +}
> +
> +static int log_thread_id(void)
> +{
> +#ifdef CONFIG_GETTID
> +    return gettid();
> +#elif defined(SYS_gettid)
> +    return syscall(SYS_gettid);
> +#else
> +    static int counter;
> +    return qatomic_fetch_inc(&counter);
> +#endif
>  }
>  
>  /* Lock/unlock output. */
> @@ -62,20 +79,34 @@ FILE *qemu_log_trylock(void)
>  {
>      FILE *logfile;
>  
> -    rcu_read_lock();
> -    /*
> -     * FIXME: typeof_strip_qual, as used by qatomic_rcu_read,
> -     * does not work with pointers to undefined structures,
> -     * such as we have with struct _IO_FILE and musl libc.
> -     * Since all we want is a read of a pointer, cast to void**,
> -     * which does work with typeof_strip_qual.
> -     */
> -    logfile = qatomic_rcu_read((void **)&global_file);
> -    if (logfile) {
> -        qemu_flockfile(logfile);
> -    } else {
> -        rcu_read_unlock();
> +    logfile = thread_file;
> +    if (!logfile) {
> +        if (log_per_thread) {
> +            g_autofree char *filename
> +                = g_strdup_printf(global_filename, log_thread_id());
> +            logfile = fopen(filename, "w");
> +            if (!logfile) {
> +                return NULL;
> +            }
> +            thread_file = logfile;
> +        } else {
> +            rcu_read_lock();
> +            /*
> +             * FIXME: typeof_strip_qual, as used by qatomic_rcu_read,
> +             * does not work with pointers to undefined structures,
> +             * such as we have with struct _IO_FILE and musl libc.
> +             * Since all we want is a read of a pointer, cast to void**,
> +             * which does work with typeof_strip_qual.
> +             */
> +            logfile = qatomic_rcu_read((void **)&global_file);
> +            if (!logfile) {
> +                rcu_read_unlock();
> +                return NULL;
> +            }
> +        }
>      }
> +
> +    qemu_flockfile(logfile);
>      return logfile;
>  }
>  
> @@ -84,7 +115,9 @@ void qemu_log_unlock(FILE *logfile)
>      if (logfile) {
>          fflush(logfile);
>          qemu_funlockfile(logfile);
> -        rcu_read_unlock();
> +        if (!log_per_thread) {
> +            rcu_read_unlock();
> +        }
>      }
>  }
>  
> @@ -112,40 +145,74 @@ static void rcu_close_file(RCUCloseFILE *r)
>      g_free(r);
>  }
>  
> +/**
> + * valid_filename_template:
> + *
> + * Validate the filename template.  Require %d if per_thread, allow it
> + * otherwise; require no other % within the template.
> + * Return 0 if invalid, 1 if stderr, 2 if strdup, 3 if pid printf.

From a neatness point of view having an enum would make it easier to
read in the switch down bellow...

> + */
> +static int valid_filename_template(const char *filename,
> +                                   bool per_thread, Error **errp)
> +{
> +    if (filename) {
> +        char *pidstr = strstr(filename, "%");
> +
> +        if (pidstr) {
> +            /* We only accept one %d, no other format strings */
> +            if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) {
> +                error_setg(errp, "Bad logfile template: %s", filename);
> +                return 0;
> +            }
> +            return per_thread ? 2 : 3;
> +        }
> +    }
> +    if (per_thread) {
> +        error_setg(errp, "Filename template with '%%d' required for 'tid'");
> +        return 0;
> +    }
> +    return filename ? 2 : 1;
> +}
> +
>  /* enable or disable low levels log */
>  static bool qemu_set_log_internal(const char *filename, bool changed_name,
>                                    int log_flags, Error **errp)
>  {
>      bool need_to_open_file;
>      bool daemonized;
> +    bool per_thread;
>      FILE *logfile;
>  
>      QEMU_LOCK_GUARD(&global_mutex);
>      logfile = global_file;
>  
> +    per_thread = log_flags & LOG_PER_THREAD;
> +
>      if (changed_name) {
>          char *newname = NULL;
>  
>          /*
> -         * Allow the user to include %d in their logfile which will be
> -         * substituted with the current PID. This is useful for debugging many
> -         * nested linux-user tasks but will result in lots of logs.
> -         *
> -         * filename may be NULL. In that case, log output is sent to stderr
> +         * Once threads start opening their own log files, we have no
> +         * easy mechanism to tell them all to close and re-open.
> +         * There seems little cause to do so either -- this option
> +         * will most often be used at user-only startup.
>           */
> -        if (filename) {
> -            char *pidstr = strstr(filename, "%");
> +        if (log_per_thread) {
> +            error_setg(errp, "Cannot change log filename after setting 'tid'");
> +            return false;
> +        }
>  
> -            if (pidstr) {
> -                /* We only accept one %d, no other format strings */
> -                if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) {
> -                    error_setg(errp, "Bad logfile format: %s", filename);
> -                    return false;
> -                }
> -                newname = g_strdup_printf(filename, getpid());
> -            } else {
> -                newname = g_strdup(filename);
> -            }
> +        switch (valid_filename_template(filename, per_thread, errp)) {
> +        case 0:
> +            return false; /* error */
> +        case 1:
> +            break;  /* stderr */
> +        case 2:
> +            newname = g_strdup(filename);
> +            break;
> +        case 3:
> +            newname = g_strdup_printf(filename, getpid());
> +            break;

default: g_assert_not_reached?

>          }
>  
>          g_free(global_filename);
> @@ -153,8 +220,17 @@ static bool qemu_set_log_internal(const char *filename, bool changed_name,
>          filename = newname;
>      } else {
>          filename = global_filename;
> +        if (per_thread && !valid_filename_template(filename, true, errp)) {
> +            return false; /* error */
> +        }
>      }
>  
> +    /* Once the per-thread flag is set, it cannot be unset. */
> +    if (per_thread) {
> +        log_per_thread = true;
> +    }
> +    /* The flag itself is not relevant for need_to_open_file. */
> +    log_flags &= ~LOG_PER_THREAD;
>  #ifdef CONFIG_TRACE_LOG
>      log_flags |= LOG_TRACE;
>  #endif
> @@ -163,12 +239,13 @@ static bool qemu_set_log_internal(const char *filename, bool changed_name,
>      /*
>       * In all cases we only log if qemu_loglevel is set.
>       * Also:
> +     *   If per-thread, open the file for each thread in qemu_log_lock.
>       *   If not daemonized we will always log either to stderr
>       *     or to a file (if there is a filename).
>       *   If we are daemonized, we will only log if there is a filename.
>       */
>      daemonized = is_daemonized();
> -    need_to_open_file = log_flags && (!daemonized || filename);
> +    need_to_open_file = log_flags && !per_thread && (!daemonized || filename);
>  
>      if (logfile && (!need_to_open_file || changed_name)) {
>          qatomic_rcu_set(&global_file, NULL);
> @@ -354,6 +431,8 @@ const QEMULogItem qemu_log_items[] = {
>  #endif
>      { LOG_STRACE, "strace",
>        "log every user-mode syscall, its input, and its result" },
> +    { LOG_PER_THREAD, "tid",
> +      "open a separate log file per thread; filename must contain '%d'" },
>      { 0, NULL, NULL },
>  };

Otherwise:

Reviewed-by: Alex Bennée <alex.bennee@linaro.org>

-- 
Alex Bennée


  reply	other threads:[~2022-04-14 15:39 UTC|newest]

Thread overview: 84+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-26 13:24 [PATCH for-7.1 v2 00/39] Logging cleanup and per-thread logfiles Richard Henderson
2022-03-26 13:24 ` [PATCH v2 1/8] linux-user/nios2: Hoist pc advance to the top of EXCP_TRAP Richard Henderson
2022-03-26 13:36   ` Richard Henderson
2022-03-26 13:24 ` [PATCH v2 01/39] util/log: Drop manual log buffering Richard Henderson
2022-03-26 13:24 ` [PATCH v2 2/8] linux-user/nios2: Fix clone child return Richard Henderson
2022-03-26 13:24 ` [PATCH v2 02/39] target/hexagon: Remove qemu_set_log in hexagon_translate_init Richard Henderson
2022-03-26 13:24 ` [PATCH v2 3/8] linux-user/nios2: Drop syscall 0 "workaround" Richard Henderson
2022-03-26 13:24 ` [PATCH v2 03/39] util/log: Return bool from qemu_set_log_filename Richard Henderson
2022-04-12 20:34   ` Alex Bennée
2022-03-26 13:24 ` [PATCH v2 4/8] linux-user/nios2: Adjust error return Richard Henderson
2022-03-26 13:24 ` [PATCH v2 04/39] util/log: Pass Error pointer to qemu_set_log Richard Henderson
2022-04-12 20:37   ` Alex Bennée
2022-03-26 13:24 ` [PATCH v2 5/8] linux-user/nios2: Handle special qemu syscall return values Richard Henderson
2022-03-26 13:24 ` [PATCH v2 05/39] os-posix: Use qemu_log_enabled Richard Henderson
2022-03-26 13:24 ` [PATCH v2 6/8] linux-user/nios2: Remove do_sigreturn Richard Henderson
2022-03-26 13:24 ` [PATCH v2 06/39] util/log: Move qemu_log_lock, qemu_log_unlock out of line Richard Henderson
2022-03-26 13:25 ` [PATCH v2 7/8] linux-user/nios2: Use QEMU_ESIGRETURN from do_rt_sigreturn Richard Henderson
2022-03-26 13:25 ` [PATCH v2 07/39] util/log: Rename qemu_log_lock to qemu_log_trylock Richard Henderson
2022-04-12 20:38   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 08/39] hw/xen: Split out xen_pv_output_msg Richard Henderson
2022-03-26 13:25 ` [PATCH v2 8/8] tests/tcg/nios2: Re-enable linux-user tests Richard Henderson
2022-03-26 13:25 ` [PATCH v2 09/39] *: Use fprintf between qemu_log_trylock/unlock Richard Henderson
2022-04-13 13:54   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 10/39] util/log: Remove qemu_log_vprintf Richard Henderson
2022-03-26 13:25 ` [PATCH v2 11/39] tcg: Pass the locked filepointer to tcg_dump_ops Richard Henderson
2022-04-13  9:08   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 12/39] exec/translator: Pass the locked filepointer to disas_log hook Richard Henderson
2022-04-13 13:55   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 13/39] exec/log: Remove log_disas and log_target_disas Richard Henderson
2022-04-13 18:41   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 14/39] accel/tcg: Use cpu_dump_state between qemu_log_trylock/unlock Richard Henderson
2022-04-14 13:46   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 15/39] target/nios2: Remove log_cpu_state from reset Richard Henderson
2022-04-14 13:47   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 16/39] util/log: Use qemu_log_trylock/unlock in qemu_log Richard Henderson
2022-03-26 13:25 ` [PATCH v2 17/39] util/log: Drop return value from qemu_log Richard Henderson
2022-04-14 14:18   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 18/39] util/log: Mark qemu_log_trylock as G_GNUC_WARN_UNUSED_RESULT Richard Henderson
2022-04-14 14:18   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 19/39] util/log: Remove qemu_log_flush Richard Henderson
2022-04-14 14:19   ` Alex Bennée
2022-04-14 14:20   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 20/39] util/log: Drop call to setvbuf Richard Henderson
2022-04-14 14:21   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 21/39] bsd-user: Expand log_page_dump inline Richard Henderson
2022-04-14 14:21   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 22/39] linux-user: " Richard Henderson
2022-04-14 14:22   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 23/39] tests/unit: Do not reference QemuLogFile directly Richard Henderson
2022-04-14 14:49   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 24/39] include/exec/log: " Richard Henderson
2022-03-26 13:25 ` [PATCH v2 25/39] include/qemu/log: Move entire implementation out-of-line Richard Henderson
2022-04-14 14:50   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 26/39] sysemu/os-win32: Test for and use _lock_file/_unlock_file Richard Henderson
2022-04-14 14:51   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 27/39] util/log: Introduce qemu_set_log_filename_flags Richard Henderson
2022-04-14 14:56   ` Alex Bennée
2022-04-14 17:32     ` Richard Henderson
2022-03-26 13:25 ` [PATCH v2 28/39] bsd-user: Use qemu_set_log_filename_flags Richard Henderson
2022-04-14 15:02   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 29/39] linux-user: " Richard Henderson
2022-04-14 15:06   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 30/39] softmmu: " Richard Henderson
2022-04-14 15:16   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 31/39] util/log: Remove qemu_log_close Richard Henderson
2022-04-14 15:16   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 32/39] util/log: Rename logfilename to global_filename Richard Henderson
2022-04-14 15:18   ` Alex Bennée
2022-04-14 15:56     ` Richard Henderson
2022-03-26 13:25 ` [PATCH v2 33/39] util/log: Rename qemu_logfile to global_file Richard Henderson
2022-04-14 15:19   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 34/39] util/log: Rename qemu_logfile_mutex to global_mutex Richard Henderson
2022-04-14 15:21   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 35/39] util/log: Hoist the eval of is_daemonized in qemu_set_log_internal Richard Henderson
2022-04-14 15:25   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 36/39] util/log: Combine two logfile closes Richard Henderson
2022-04-14 15:26   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 37/39] util/log: Rename QemuLogFile to RCUCloseFILE Richard Henderson
2022-04-14 15:27   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 38/39] util/log: Limit RCUCloseFILE to file closing Richard Henderson
2022-04-14 15:30   ` Alex Bennée
2022-03-26 13:25 ` [PATCH v2 39/39] util/log: Support per-thread log files Richard Henderson
2022-04-14 15:35   ` Alex Bennée [this message]
2022-04-14 16:32     ` 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=87y207smjt.fsf@linaro.org \
    --to=alex.bennee@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=richard.henderson@linaro.org \
    /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.