qemu/util/qemu-error.c
<<
>>
Prefs
   1/*
   2 * Error reporting
   3 *
   4 * Copyright (C) 2010 Red Hat Inc.
   5 *
   6 * Authors:
   7 *  Markus Armbruster <armbru@redhat.com>,
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "monitor/monitor.h"
  15#include "qemu/error-report.h"
  16
  17/*
  18 * @report_type is the type of message: error, warning or
  19 * informational.
  20 */
  21typedef enum {
  22    REPORT_TYPE_ERROR,
  23    REPORT_TYPE_WARNING,
  24    REPORT_TYPE_INFO,
  25} report_type;
  26
  27/* Prepend timestamp to messages */
  28bool message_with_timestamp;
  29bool error_with_guestname;
  30const char *error_guest_name;
  31
  32int error_printf(const char *fmt, ...)
  33{
  34    va_list ap;
  35    int ret;
  36
  37    va_start(ap, fmt);
  38    ret = error_vprintf(fmt, ap);
  39    va_end(ap);
  40    return ret;
  41}
  42
  43int error_printf_unless_qmp(const char *fmt, ...)
  44{
  45    va_list ap;
  46    int ret;
  47
  48    va_start(ap, fmt);
  49    ret = error_vprintf_unless_qmp(fmt, ap);
  50    va_end(ap);
  51    return ret;
  52}
  53
  54static Location std_loc = {
  55    .kind = LOC_NONE
  56};
  57static Location *cur_loc = &std_loc;
  58
  59/*
  60 * Push location saved in LOC onto the location stack, return it.
  61 * The top of that stack is the current location.
  62 * Needs a matching loc_pop().
  63 */
  64Location *loc_push_restore(Location *loc)
  65{
  66    assert(!loc->prev);
  67    loc->prev = cur_loc;
  68    cur_loc = loc;
  69    return loc;
  70}
  71
  72/*
  73 * Initialize *LOC to "nowhere", push it onto the location stack.
  74 * The top of that stack is the current location.
  75 * Needs a matching loc_pop().
  76 * Return LOC.
  77 */
  78Location *loc_push_none(Location *loc)
  79{
  80    loc->kind = LOC_NONE;
  81    loc->prev = NULL;
  82    return loc_push_restore(loc);
  83}
  84
  85/*
  86 * Pop the location stack.
  87 * LOC must be the current location, i.e. the top of the stack.
  88 */
  89Location *loc_pop(Location *loc)
  90{
  91    assert(cur_loc == loc && loc->prev);
  92    cur_loc = loc->prev;
  93    loc->prev = NULL;
  94    return loc;
  95}
  96
  97/*
  98 * Save the current location in LOC, return LOC.
  99 */
 100Location *loc_save(Location *loc)
 101{
 102    *loc = *cur_loc;
 103    loc->prev = NULL;
 104    return loc;
 105}
 106
 107/*
 108 * Change the current location to the one saved in LOC.
 109 */
 110void loc_restore(Location *loc)
 111{
 112    Location *prev = cur_loc->prev;
 113    assert(!loc->prev);
 114    *cur_loc = *loc;
 115    cur_loc->prev = prev;
 116}
 117
 118/*
 119 * Change the current location to "nowhere in particular".
 120 */
 121void loc_set_none(void)
 122{
 123    cur_loc->kind = LOC_NONE;
 124}
 125
 126/*
 127 * Change the current location to argument ARGV[IDX..IDX+CNT-1].
 128 */
 129void loc_set_cmdline(char **argv, int idx, int cnt)
 130{
 131    cur_loc->kind = LOC_CMDLINE;
 132    cur_loc->num = cnt;
 133    cur_loc->ptr = argv + idx;
 134}
 135
 136/*
 137 * Change the current location to file FNAME, line LNO.
 138 */
 139void loc_set_file(const char *fname, int lno)
 140{
 141    assert (fname || cur_loc->kind == LOC_FILE);
 142    cur_loc->kind = LOC_FILE;
 143    cur_loc->num = lno;
 144    if (fname) {
 145        cur_loc->ptr = fname;
 146    }
 147}
 148
 149static const char *progname;
 150
 151/*
 152 * Set the program name for error_print_loc().
 153 */
 154static void error_set_progname(const char *argv0)
 155{
 156    const char *p = strrchr(argv0, '/');
 157    progname = p ? p + 1 : argv0;
 158}
 159
 160const char *error_get_progname(void)
 161{
 162    return progname;
 163}
 164
 165/*
 166 * Print current location to current monitor if we have one, else to stderr.
 167 */
 168static void print_loc(void)
 169{
 170    const char *sep = "";
 171    int i;
 172    const char *const *argp;
 173
 174    if (!monitor_cur() && progname) {
 175        fprintf(stderr, "%s:", progname);
 176        sep = " ";
 177    }
 178    switch (cur_loc->kind) {
 179    case LOC_CMDLINE:
 180        argp = cur_loc->ptr;
 181        for (i = 0; i < cur_loc->num; i++) {
 182            error_printf("%s%s", sep, argp[i]);
 183            sep = " ";
 184        }
 185        error_printf(": ");
 186        break;
 187    case LOC_FILE:
 188        error_printf("%s:", (const char *)cur_loc->ptr);
 189        if (cur_loc->num) {
 190            error_printf("%d:", cur_loc->num);
 191        }
 192        error_printf(" ");
 193        break;
 194    default:
 195        error_printf("%s", sep);
 196    }
 197}
 198
 199/*
 200 * Print a message to current monitor if we have one, else to stderr.
 201 * @report_type is the type of message: error, warning or informational.
 202 * Format arguments like vsprintf().  The resulting message should be
 203 * a single phrase, with no newline or trailing punctuation.
 204 * Prepend the current location and append a newline.
 205 */
 206static void vreport(report_type type, const char *fmt, va_list ap)
 207{
 208    GTimeVal tv;
 209    gchar *timestr;
 210
 211    if (message_with_timestamp && !monitor_cur()) {
 212        g_get_current_time(&tv);
 213        timestr = g_time_val_to_iso8601(&tv);
 214        error_printf("%s ", timestr);
 215        g_free(timestr);
 216    }
 217
 218    /* Only prepend guest name if -msg guest-name and -name guest=... are set */
 219    if (error_with_guestname && error_guest_name && !monitor_cur()) {
 220        error_printf("%s ", error_guest_name);
 221    }
 222
 223    print_loc();
 224
 225    switch (type) {
 226    case REPORT_TYPE_ERROR:
 227        break;
 228    case REPORT_TYPE_WARNING:
 229        error_printf("warning: ");
 230        break;
 231    case REPORT_TYPE_INFO:
 232        error_printf("info: ");
 233        break;
 234    }
 235
 236    error_vprintf(fmt, ap);
 237    error_printf("\n");
 238}
 239
 240/*
 241 * Print an error message to current monitor if we have one, else to stderr.
 242 * Format arguments like vsprintf().  The resulting message should be
 243 * a single phrase, with no newline or trailing punctuation.
 244 * Prepend the current location and append a newline.
 245 * It's wrong to call this in a QMP monitor.  Use error_setg() there.
 246 */
 247void error_vreport(const char *fmt, va_list ap)
 248{
 249    vreport(REPORT_TYPE_ERROR, fmt, ap);
 250}
 251
 252/*
 253 * Print a warning message to current monitor if we have one, else to stderr.
 254 * Format arguments like vsprintf().  The resulting message should be
 255 * a single phrase, with no newline or trailing punctuation.
 256 * Prepend the current location and append a newline.
 257 */
 258void warn_vreport(const char *fmt, va_list ap)
 259{
 260    vreport(REPORT_TYPE_WARNING, fmt, ap);
 261}
 262
 263/*
 264 * Print an information message to current monitor if we have one, else to
 265 * stderr.
 266 * Format arguments like vsprintf().  The resulting message should be
 267 * a single phrase, with no newline or trailing punctuation.
 268 * Prepend the current location and append a newline.
 269 */
 270void info_vreport(const char *fmt, va_list ap)
 271{
 272    vreport(REPORT_TYPE_INFO, fmt, ap);
 273}
 274
 275/*
 276 * Print an error message to current monitor if we have one, else to stderr.
 277 * Format arguments like sprintf().  The resulting message should be
 278 * a single phrase, with no newline or trailing punctuation.
 279 * Prepend the current location and append a newline.
 280 * It's wrong to call this in a QMP monitor.  Use error_setg() there.
 281 */
 282void error_report(const char *fmt, ...)
 283{
 284    va_list ap;
 285
 286    va_start(ap, fmt);
 287    vreport(REPORT_TYPE_ERROR, fmt, ap);
 288    va_end(ap);
 289}
 290
 291/*
 292 * Print a warning message to current monitor if we have one, else to stderr.
 293 * Format arguments like sprintf(). The resulting message should be a
 294 * single phrase, with no newline or trailing punctuation.
 295 * Prepend the current location and append a newline.
 296 */
 297void warn_report(const char *fmt, ...)
 298{
 299    va_list ap;
 300
 301    va_start(ap, fmt);
 302    vreport(REPORT_TYPE_WARNING, fmt, ap);
 303    va_end(ap);
 304}
 305
 306/*
 307 * Print an information message to current monitor if we have one, else to
 308 * stderr.
 309 * Format arguments like sprintf(). The resulting message should be a
 310 * single phrase, with no newline or trailing punctuation.
 311 * Prepend the current location and append a newline.
 312 */
 313void info_report(const char *fmt, ...)
 314{
 315    va_list ap;
 316
 317    va_start(ap, fmt);
 318    vreport(REPORT_TYPE_INFO, fmt, ap);
 319    va_end(ap);
 320}
 321
 322/*
 323 * Like error_report(), except print just once.
 324 * If *printed is false, print the message, and flip *printed to true.
 325 * Return whether the message was printed.
 326 */
 327bool error_report_once_cond(bool *printed, const char *fmt, ...)
 328{
 329    va_list ap;
 330
 331    assert(printed);
 332    if (*printed) {
 333        return false;
 334    }
 335    *printed = true;
 336    va_start(ap, fmt);
 337    vreport(REPORT_TYPE_ERROR, fmt, ap);
 338    va_end(ap);
 339    return true;
 340}
 341
 342/*
 343 * Like warn_report(), except print just once.
 344 * If *printed is false, print the message, and flip *printed to true.
 345 * Return whether the message was printed.
 346 */
 347bool warn_report_once_cond(bool *printed, const char *fmt, ...)
 348{
 349    va_list ap;
 350
 351    assert(printed);
 352    if (*printed) {
 353        return false;
 354    }
 355    *printed = true;
 356    va_start(ap, fmt);
 357    vreport(REPORT_TYPE_WARNING, fmt, ap);
 358    va_end(ap);
 359    return true;
 360}
 361
 362static char *qemu_glog_domains;
 363
 364static void qemu_log_func(const gchar *log_domain,
 365                          GLogLevelFlags log_level,
 366                          const gchar *message,
 367                          gpointer user_data)
 368{
 369    switch (log_level & G_LOG_LEVEL_MASK) {
 370    case G_LOG_LEVEL_DEBUG:
 371    case G_LOG_LEVEL_INFO:
 372        /*
 373         * Use same G_MESSAGES_DEBUG logic as glib to enable/disable debug
 374         * messages
 375         */
 376        if (qemu_glog_domains == NULL) {
 377            break;
 378        }
 379        if (strcmp(qemu_glog_domains, "all") != 0 &&
 380          (log_domain == NULL || !strstr(qemu_glog_domains, log_domain))) {
 381            break;
 382        }
 383        /* Fall through */
 384    case G_LOG_LEVEL_MESSAGE:
 385        info_report("%s%s%s",
 386                    log_domain ?: "", log_domain ? ": " : "", message);
 387
 388        break;
 389    case G_LOG_LEVEL_WARNING:
 390        warn_report("%s%s%s",
 391                    log_domain ?: "", log_domain ? ": " : "", message);
 392        break;
 393    case G_LOG_LEVEL_CRITICAL:
 394    case G_LOG_LEVEL_ERROR:
 395        error_report("%s%s%s",
 396                     log_domain ?: "", log_domain ? ": " : "", message);
 397        break;
 398    }
 399}
 400
 401void error_init(const char *argv0)
 402{
 403    /* Set the program name for error_print_loc(). */
 404    error_set_progname(argv0);
 405
 406    /*
 407     * This sets up glib logging so libraries using it also print their logs
 408     * through error_report(), warn_report(), info_report().
 409     */
 410    g_log_set_default_handler(qemu_log_func, NULL);
 411    g_warn_if_fail(qemu_glog_domains == NULL);
 412    qemu_glog_domains = g_strdup(g_getenv("G_MESSAGES_DEBUG"));
 413}
 414