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