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 "qemu/cutils.h"
  26#include "trace/control.h"
  27
  28static char *logfilename;
  29FILE *qemu_logfile;
  30int qemu_loglevel;
  31static int log_append = 0;
  32static GArray *debug_regions;
  33
  34void qemu_log(const char *fmt, ...)
  35{
  36    va_list ap;
  37
  38    va_start(ap, fmt);
  39    if (qemu_logfile) {
  40        vfprintf(qemu_logfile, fmt, ap);
  41    }
  42    va_end(ap);
  43}
  44
  45/* enable or disable low levels log */
  46void do_qemu_set_log(int log_flags, bool use_own_buffers)
  47{
  48    qemu_loglevel = log_flags;
  49#ifdef CONFIG_TRACE_LOG
  50    qemu_loglevel |= LOG_TRACE;
  51#endif
  52    if (!qemu_logfile &&
  53        (is_daemonized() ? logfilename != NULL : qemu_loglevel)) {
  54        if (logfilename) {
  55            qemu_logfile = fopen(logfilename, log_append ? "a" : "w");
  56            if (!qemu_logfile) {
  57                perror(logfilename);
  58                _exit(1);
  59            }
  60            /* In case we are a daemon redirect stderr to logfile */
  61            if (is_daemonized()) {
  62                dup2(fileno(qemu_logfile), STDERR_FILENO);
  63                fclose(qemu_logfile);
  64                /* This will skip closing logfile in qemu_log_close() */
  65                qemu_logfile = stderr;
  66            }
  67        } else {
  68            /* Default to stderr if no log file specified */
  69            assert(!is_daemonized());
  70            qemu_logfile = stderr;
  71        }
  72        /* must avoid mmap() usage of glibc by setting a buffer "by hand" */
  73        if (use_own_buffers) {
  74            static char logfile_buf[4096];
  75
  76            setvbuf(qemu_logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
  77        } else {
  78#if defined(_WIN32)
  79            /* Win32 doesn't support line-buffering, so use unbuffered output. */
  80            setvbuf(qemu_logfile, NULL, _IONBF, 0);
  81#else
  82            setvbuf(qemu_logfile, NULL, _IOLBF, 0);
  83#endif
  84            log_append = 1;
  85        }
  86    }
  87    if (qemu_logfile &&
  88        (is_daemonized() ? logfilename == NULL : !qemu_loglevel)) {
  89        qemu_log_close();
  90    }
  91}
  92/*
  93 * Allow the user to include %d in their logfile which will be
  94 * substituted with the current PID. This is useful for debugging many
  95 * nested linux-user tasks but will result in lots of logs.
  96 */
  97void qemu_set_log_filename(const char *filename)
  98{
  99    char *pidstr;
 100    g_free(logfilename);
 101
 102    pidstr = strstr(filename, "%");
 103    if (pidstr) {
 104        /* We only accept one %d, no other format strings */
 105        if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) {
 106            error_report("Bad logfile format: %s", filename);
 107            logfilename = NULL;
 108        } else {
 109            logfilename = g_strdup_printf(filename, getpid());
 110        }
 111    } else {
 112        logfilename = g_strdup(filename);
 113    }
 114    qemu_log_close();
 115    qemu_set_log(qemu_loglevel);
 116}
 117
 118/* Returns true if addr is in our debug filter or no filter defined
 119 */
 120bool qemu_log_in_addr_range(uint64_t addr)
 121{
 122    if (debug_regions) {
 123        int i = 0;
 124        for (i = 0; i < debug_regions->len; i++) {
 125            struct Range *range = &g_array_index(debug_regions, Range, i);
 126            if (addr >= range->begin && addr <= range->end) {
 127                return true;
 128            }
 129        }
 130        return false;
 131    } else {
 132        return true;
 133    }
 134}
 135
 136
 137void qemu_set_dfilter_ranges(const char *filter_spec)
 138{
 139    gchar **ranges = g_strsplit(filter_spec, ",", 0);
 140    if (ranges) {
 141        gchar **next = ranges;
 142        gchar *r = *next++;
 143        debug_regions = g_array_sized_new(FALSE, FALSE,
 144                                          sizeof(Range), g_strv_length(ranges));
 145        while (r) {
 146            char *range_op = strstr(r, "-");
 147            char *r2 = range_op ? range_op + 1 : NULL;
 148            if (!range_op) {
 149                range_op = strstr(r, "+");
 150                r2 = range_op ? range_op + 1 : NULL;
 151            }
 152            if (!range_op) {
 153                range_op = strstr(r, "..");
 154                r2 = range_op ? range_op + 2 : NULL;
 155            }
 156            if (range_op) {
 157                const char *e = NULL;
 158                uint64_t r1val, r2val;
 159
 160                if ((qemu_strtoull(r, &e, 0, &r1val) == 0) &&
 161                    (qemu_strtoull(r2, NULL, 0, &r2val) == 0) &&
 162                    r2val > 0) {
 163                    struct Range range;
 164
 165                    g_assert(e == range_op);
 166
 167                    switch (*range_op) {
 168                    case '+':
 169                    {
 170                        range.begin = r1val;
 171                        range.end = r1val + (r2val - 1);
 172                        break;
 173                    }
 174                    case '-':
 175                    {
 176                        range.end = r1val;
 177                        range.begin = r1val - (r2val - 1);
 178                        break;
 179                    }
 180                    case '.':
 181                        range.begin = r1val;
 182                        range.end = r2val;
 183                        break;
 184                    default:
 185                        g_assert_not_reached();
 186                    }
 187                    g_array_append_val(debug_regions, range);
 188
 189                } else {
 190                    g_error("Failed to parse range in: %s", r);
 191                }
 192            } else {
 193                g_error("Bad range specifier in: %s", r);
 194            }
 195            r = *next++;
 196        }
 197        g_strfreev(ranges);
 198    }
 199}
 200
 201/* fflush() the log file */
 202void qemu_log_flush(void)
 203{
 204    fflush(qemu_logfile);
 205}
 206
 207/* Close the log file */
 208void qemu_log_close(void)
 209{
 210    if (qemu_logfile) {
 211        if (qemu_logfile != stderr) {
 212            fclose(qemu_logfile);
 213        }
 214        qemu_logfile = NULL;
 215    }
 216}
 217
 218const QEMULogItem qemu_log_items[] = {
 219    { CPU_LOG_TB_OUT_ASM, "out_asm",
 220      "show generated host assembly code for each compiled TB" },
 221    { CPU_LOG_TB_IN_ASM, "in_asm",
 222      "show target assembly code for each compiled TB" },
 223    { CPU_LOG_TB_OP, "op",
 224      "show micro ops for each compiled TB" },
 225    { CPU_LOG_TB_OP_OPT, "op_opt",
 226      "show micro ops (x86 only: before eflags optimization) and\n"
 227      "after liveness analysis" },
 228    { CPU_LOG_INT, "int",
 229      "show interrupts/exceptions in short format" },
 230    { CPU_LOG_EXEC, "exec",
 231      "show trace before each executed TB (lots of logs)" },
 232    { CPU_LOG_TB_CPU, "cpu",
 233      "show CPU registers before entering a TB (lots of logs)" },
 234    { CPU_LOG_MMU, "mmu",
 235      "log MMU-related activities" },
 236    { CPU_LOG_PCALL, "pcall",
 237      "x86 only: show protected mode far calls/returns/exceptions" },
 238    { CPU_LOG_RESET, "cpu_reset",
 239      "show CPU state before CPU resets" },
 240    { LOG_UNIMP, "unimp",
 241      "log unimplemented functionality" },
 242    { LOG_GUEST_ERROR, "guest_errors",
 243      "log when the guest OS does something invalid (eg accessing a\n"
 244      "non-existent register)" },
 245    { CPU_LOG_PAGE, "page",
 246      "dump pages at beginning of user mode emulation" },
 247    { CPU_LOG_TB_NOCHAIN, "nochain",
 248      "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n"
 249      "complete traces" },
 250
 251    { LOG_FDT, "fdt", "log Device Tree info." },
 252    { LOG_PM,  "pm", "log Power Management info." },
 253
 254    /* device entries */
 255    { DEV_LOG_NET_DEV, "net-dev", "enable Network Device logs." },
 256    { DEV_LOG_NAND, "nand", "enable NAND log." },
 257    { DEV_LOG_NANDC, "nandc", "enable NAND Controller log." },
 258    { DEV_LOG_SD, "sd", "enable SD/MMC card log." },
 259    { DEV_LOG_SDHCI, "sdhci", "enable SDHCI log." },
 260    { DEV_LOG_SPI, "spi", "enable SPI controller log." },
 261    { DEV_LOG_SPI_DEV, "spi-dev", "enable SPI device logs." },
 262
 263    { 0, NULL, NULL },
 264};
 265
 266static int cmp1(const char *s1, int n, const char *s2)
 267{
 268    if (strlen(s2) != n) {
 269        return 0;
 270    }
 271    return memcmp(s1, s2, n) == 0;
 272}
 273
 274/* takes a comma separated list of log masks. Return 0 if error. */
 275int qemu_str_to_log_mask(const char *str)
 276{
 277    const QEMULogItem *item;
 278    int mask;
 279    const char *p, *p1;
 280
 281    p = str;
 282    mask = 0;
 283    for (;;) {
 284        p1 = strchr(p, ',');
 285        if (!p1) {
 286            p1 = p + strlen(p);
 287        }
 288        if (cmp1(p,p1-p,"all")) {
 289            for (item = qemu_log_items; item->mask != 0; item++) {
 290                mask |= item->mask;
 291            }
 292#ifdef CONFIG_TRACE_LOG
 293        } else if (strncmp(p, "trace:", 6) == 0 && p + 6 != p1) {
 294            trace_enable_events(p + 6);
 295            mask |= LOG_TRACE;
 296#endif
 297        } else {
 298            for (item = qemu_log_items; item->mask != 0; item++) {
 299                if (cmp1(p, p1 - p, item->name)) {
 300                    goto found;
 301                }
 302            }
 303            return 0;
 304        found:
 305            mask |= item->mask;
 306        }
 307        if (*p1 != ',') {
 308            break;
 309        }
 310        p = p1 + 1;
 311    }
 312    return mask;
 313}
 314
 315void qemu_print_log_usage(FILE *f)
 316{
 317    const QEMULogItem *item;
 318    fprintf(f, "Log items (comma separated):\n");
 319    for (item = qemu_log_items; item->mask != 0; item++) {
 320        fprintf(f, "%-15s %s\n", item->name, item->help);
 321    }
 322#ifdef CONFIG_TRACE_LOG
 323    fprintf(f, "trace:PATTERN   enable trace events\n");
 324    fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n");
 325#endif
 326}
 327