qemu/util/log.c
<<
>>
Prefs
   1/*
   2 * Logging support
   3 *
   4 *  Copyright (c) 2003 Fabrice Bellard
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "qemu/osdep.h"
  21#include "qemu-common.h"
  22#include "qemu/log.h"
  23#include "qemu/range.h"
  24#include "qemu/error-report.h"
  25#include "qapi/error.h"
  26#include "qemu/cutils.h"
  27#include "trace/control.h"
  28
  29static char *logfilename;
  30FILE *qemu_logfile;
  31int qemu_loglevel;
  32static int log_append = 0;
  33static GArray *debug_regions;
  34
  35/* Return the number of characters emitted.  */
  36int qemu_log(const char *fmt, ...)
  37{
  38    int ret = 0;
  39    if (qemu_logfile) {
  40        va_list ap;
  41        va_start(ap, fmt);
  42        ret = vfprintf(qemu_logfile, fmt, ap);
  43        va_end(ap);
  44
  45        /* Don't pass back error results.  */
  46        if (ret < 0) {
  47            ret = 0;
  48        }
  49    }
  50    return ret;
  51}
  52
  53static bool log_uses_own_buffers;
  54
  55/* enable or disable low levels log */
  56void qemu_set_log(int log_flags)
  57{
  58    qemu_loglevel = log_flags;
  59#ifdef CONFIG_TRACE_LOG
  60    qemu_loglevel |= LOG_TRACE;
  61#endif
  62    if (!qemu_logfile &&
  63        (is_daemonized() ? logfilename != NULL : qemu_loglevel)) {
  64        if (logfilename) {
  65            qemu_logfile = fopen(logfilename, log_append ? "a" : "w");
  66            if (!qemu_logfile) {
  67                perror(logfilename);
  68                _exit(1);
  69            }
  70            /* In case we are a daemon redirect stderr to logfile */
  71            if (is_daemonized()) {
  72                dup2(fileno(qemu_logfile), STDERR_FILENO);
  73                fclose(qemu_logfile);
  74                /* This will skip closing logfile in qemu_log_close() */
  75                qemu_logfile = stderr;
  76            }
  77        } else {
  78            /* Default to stderr if no log file specified */
  79            assert(!is_daemonized());
  80            qemu_logfile = stderr;
  81        }
  82        /* must avoid mmap() usage of glibc by setting a buffer "by hand" */
  83        if (log_uses_own_buffers) {
  84            static char logfile_buf[4096];
  85
  86            setvbuf(qemu_logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
  87        } else {
  88#if defined(_WIN32)
  89            /* Win32 doesn't support line-buffering, so use unbuffered output. */
  90            setvbuf(qemu_logfile, NULL, _IONBF, 0);
  91#else
  92            setvbuf(qemu_logfile, NULL, _IOLBF, 0);
  93#endif
  94            log_append = 1;
  95        }
  96    }
  97    if (qemu_logfile &&
  98        (is_daemonized() ? logfilename == NULL : !qemu_loglevel)) {
  99        qemu_log_close();
 100    }
 101}
 102
 103void qemu_log_needs_buffers(void)
 104{
 105    log_uses_own_buffers = true;
 106}
 107
 108/*
 109 * Allow the user to include %d in their logfile which will be
 110 * substituted with the current PID. This is useful for debugging many
 111 * nested linux-user tasks but will result in lots of logs.
 112 */
 113void qemu_set_log_filename(const char *filename, Error **errp)
 114{
 115    char *pidstr;
 116    g_free(logfilename);
 117
 118    pidstr = strstr(filename, "%");
 119    if (pidstr) {
 120        /* We only accept one %d, no other format strings */
 121        if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) {
 122            error_setg(errp, "Bad logfile format: %s", filename);
 123            return;
 124        } else {
 125            logfilename = g_strdup_printf(filename, getpid());
 126        }
 127    } else {
 128        logfilename = g_strdup(filename);
 129    }
 130    qemu_log_close();
 131    qemu_set_log(qemu_loglevel);
 132}
 133
 134/* Returns true if addr is in our debug filter or no filter defined
 135 */
 136bool qemu_log_in_addr_range(uint64_t addr)
 137{
 138    if (debug_regions) {
 139        int i = 0;
 140        for (i = 0; i < debug_regions->len; i++) {
 141            Range *range = &g_array_index(debug_regions, Range, i);
 142            if (range_contains(range, addr)) {
 143                return true;
 144            }
 145        }
 146        return false;
 147    } else {
 148        return true;
 149    }
 150}
 151
 152
 153void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp)
 154{
 155    gchar **ranges = g_strsplit(filter_spec, ",", 0);
 156    int i;
 157
 158    if (debug_regions) {
 159        g_array_unref(debug_regions);
 160        debug_regions = NULL;
 161    }
 162
 163    debug_regions = g_array_sized_new(FALSE, FALSE,
 164                                      sizeof(Range), g_strv_length(ranges));
 165    for (i = 0; ranges[i]; i++) {
 166        const char *r = ranges[i];
 167        const char *range_op, *r2, *e;
 168        uint64_t r1val, r2val, lob, upb;
 169        struct Range range;
 170
 171        range_op = strstr(r, "-");
 172        r2 = range_op ? range_op + 1 : NULL;
 173        if (!range_op) {
 174            range_op = strstr(r, "+");
 175            r2 = range_op ? range_op + 1 : NULL;
 176        }
 177        if (!range_op) {
 178            range_op = strstr(r, "..");
 179            r2 = range_op ? range_op + 2 : NULL;
 180        }
 181        if (!range_op) {
 182            error_setg(errp, "Bad range specifier");
 183            goto out;
 184        }
 185
 186        if (qemu_strtou64(r, &e, 0, &r1val)
 187            || e != range_op) {
 188            error_setg(errp, "Invalid number to the left of %.*s",
 189                       (int)(r2 - range_op), range_op);
 190            goto out;
 191        }
 192        if (qemu_strtou64(r2, NULL, 0, &r2val)) {
 193            error_setg(errp, "Invalid number to the right of %.*s",
 194                       (int)(r2 - range_op), range_op);
 195            goto out;
 196        }
 197
 198        switch (*range_op) {
 199        case '+':
 200            lob = r1val;
 201            upb = r1val + r2val - 1;
 202            break;
 203        case '-':
 204            upb = r1val;
 205            lob = r1val - (r2val - 1);
 206            break;
 207        case '.':
 208            lob = r1val;
 209            upb = r2val;
 210            break;
 211        default:
 212            g_assert_not_reached();
 213        }
 214        if (lob > upb) {
 215            error_setg(errp, "Invalid range");
 216            goto out;
 217        }
 218        range_set_bounds(&range, lob, upb);
 219        g_array_append_val(debug_regions, range);
 220    }
 221out:
 222    g_strfreev(ranges);
 223}
 224
 225/* fflush() the log file */
 226void qemu_log_flush(void)
 227{
 228    fflush(qemu_logfile);
 229}
 230
 231/* Close the log file */
 232void qemu_log_close(void)
 233{
 234    if (qemu_logfile) {
 235        if (qemu_logfile != stderr) {
 236            fclose(qemu_logfile);
 237        }
 238        qemu_logfile = NULL;
 239    }
 240}
 241
 242const QEMULogItem qemu_log_items[] = {
 243    { CPU_LOG_TB_OUT_ASM, "out_asm",
 244      "show generated host assembly code for each compiled TB" },
 245    { CPU_LOG_TB_IN_ASM, "in_asm",
 246      "show target assembly code for each compiled TB" },
 247    { CPU_LOG_TB_OP, "op",
 248      "show micro ops for each compiled TB" },
 249    { CPU_LOG_TB_OP_OPT, "op_opt",
 250      "show micro ops after optimization" },
 251    { CPU_LOG_TB_OP_IND, "op_ind",
 252      "show micro ops before indirect lowering" },
 253    { CPU_LOG_INT, "int",
 254      "show interrupts/exceptions in short format" },
 255    { CPU_LOG_EXEC, "exec",
 256      "show trace before each executed TB (lots of logs)" },
 257    { CPU_LOG_TB_CPU, "cpu",
 258      "show CPU registers before entering a TB (lots of logs)" },
 259    { CPU_LOG_MMU, "mmu",
 260      "log MMU-related activities" },
 261    { CPU_LOG_PCALL, "pcall",
 262      "x86 only: show protected mode far calls/returns/exceptions" },
 263    { CPU_LOG_RESET, "cpu_reset",
 264      "show CPU state before CPU resets" },
 265    { LOG_UNIMP, "unimp",
 266      "log unimplemented functionality" },
 267    { LOG_GUEST_ERROR, "guest_errors",
 268      "log when the guest OS does something invalid (eg accessing a\n"
 269      "non-existent register)" },
 270    { CPU_LOG_PAGE, "page",
 271      "dump pages at beginning of user mode emulation" },
 272    { CPU_LOG_TB_NOCHAIN, "nochain",
 273      "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n"
 274      "complete traces" },
 275    { 0, NULL, NULL },
 276};
 277
 278/* takes a comma separated list of log masks. Return 0 if error. */
 279int qemu_str_to_log_mask(const char *str)
 280{
 281    const QEMULogItem *item;
 282    int mask = 0;
 283    char **parts = g_strsplit(str, ",", 0);
 284    char **tmp;
 285
 286    for (tmp = parts; tmp && *tmp; tmp++) {
 287        if (g_str_equal(*tmp, "all")) {
 288            for (item = qemu_log_items; item->mask != 0; item++) {
 289                mask |= item->mask;
 290            }
 291#ifdef CONFIG_TRACE_LOG
 292        } else if (g_str_has_prefix(*tmp, "trace:") && (*tmp)[6] != '\0') {
 293            trace_enable_events((*tmp) + 6);
 294            mask |= LOG_TRACE;
 295#endif
 296        } else {
 297            for (item = qemu_log_items; item->mask != 0; item++) {
 298                if (g_str_equal(*tmp, item->name)) {
 299                    goto found;
 300                }
 301            }
 302            goto error;
 303        found:
 304            mask |= item->mask;
 305        }
 306    }
 307
 308    g_strfreev(parts);
 309    return mask;
 310
 311 error:
 312    g_strfreev(parts);
 313    return 0;
 314}
 315
 316void qemu_print_log_usage(FILE *f)
 317{
 318    const QEMULogItem *item;
 319    fprintf(f, "Log items (comma separated):\n");
 320    for (item = qemu_log_items; item->mask != 0; item++) {
 321        fprintf(f, "%-15s %s\n", item->name, item->help);
 322    }
 323#ifdef CONFIG_TRACE_LOG
 324    fprintf(f, "trace:PATTERN   enable trace events\n");
 325    fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n");
 326#endif
 327}
 328