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 */
 196static void vreport(report_type type, const char *fmt, va_list ap)
 197{
 198    gchar *timestr;
 199
 200    if (message_with_timestamp && !monitor_cur()) {
 201        timestr = real_time_iso8601();
 202        error_printf("%s ", timestr);
 203        g_free(timestr);
 204    }
 205
 206    /* Only prepend guest name if -msg guest-name and -name guest=... are set */
 207    if (error_with_guestname && error_guest_name && !monitor_cur()) {
 208        error_printf("%s ", error_guest_name);
 209    }
 210
 211    print_loc();
 212
 213    switch (type) {
 214    case REPORT_TYPE_ERROR:
 215        break;
 216    case REPORT_TYPE_WARNING:
 217        error_printf("warning: ");
 218        break;
 219    case REPORT_TYPE_INFO:
 220        error_printf("info: ");
 221        break;
 222    }
 223
 224    error_vprintf(fmt, ap);
 225    error_printf("\n");
 226}
 227
 228/*
 229 * Print an error message to current monitor if we have one, else to stderr.
 230 * Format arguments like vsprintf().  The resulting message should be
 231 * a single phrase, with no newline or trailing punctuation.
 232 * Prepend the current location and append a newline.
 233 * It's wrong to call this in a QMP monitor.  Use error_setg() there.
 234 */
 235void error_vreport(const char *fmt, va_list ap)
 236{
 237    vreport(REPORT_TYPE_ERROR, fmt, ap);
 238}
 239
 240/*
 241 * Print a warning 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 */
 246void warn_vreport(const char *fmt, va_list ap)
 247{
 248    vreport(REPORT_TYPE_WARNING, fmt, ap);
 249}
 250
 251/*
 252 * Print an information message to current monitor if we have one, else to
 253 * 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 info_vreport(const char *fmt, va_list ap)
 259{
 260    vreport(REPORT_TYPE_INFO, fmt, ap);
 261}
 262
 263/*
 264 * Print an error message to current monitor if we have one, else to stderr.
 265 * Format arguments like sprintf().  The resulting message should be
 266 * a single phrase, with no newline or trailing punctuation.
 267 * Prepend the current location and append a newline.
 268 * It's wrong to call this in a QMP monitor.  Use error_setg() there.
 269 */
 270void error_report(const char *fmt, ...)
 271{
 272    va_list ap;
 273
 274    va_start(ap, fmt);
 275    vreport(REPORT_TYPE_ERROR, fmt, ap);
 276    va_end(ap);
 277}
 278
 279/*
 280 * Print a warning message to current monitor if we have one, else to stderr.
 281 * Format arguments like sprintf(). The resulting message should be a
 282 * single phrase, with no newline or trailing punctuation.
 283 * Prepend the current location and append a newline.
 284 */
 285void warn_report(const char *fmt, ...)
 286{
 287    va_list ap;
 288
 289    va_start(ap, fmt);
 290    vreport(REPORT_TYPE_WARNING, fmt, ap);
 291    va_end(ap);
 292}
 293
 294/*
 295 * Print an information message to current monitor if we have one, else to
 296 * stderr.
 297 * Format arguments like sprintf(). The resulting message should be a
 298 * single phrase, with no newline or trailing punctuation.
 299 * Prepend the current location and append a newline.
 300 */
 301void info_report(const char *fmt, ...)
 302{
 303    va_list ap;
 304
 305    va_start(ap, fmt);
 306    vreport(REPORT_TYPE_INFO, fmt, ap);
 307    va_end(ap);
 308}
 309
 310/*
 311 * Like error_report(), except print just once.
 312 * If *printed is false, print the message, and flip *printed to true.
 313 * Return whether the message was printed.
 314 */
 315bool error_report_once_cond(bool *printed, const char *fmt, ...)
 316{
 317    va_list ap;
 318
 319    assert(printed);
 320    if (*printed) {
 321        return false;
 322    }
 323    *printed = true;
 324    va_start(ap, fmt);
 325    vreport(REPORT_TYPE_ERROR, fmt, ap);
 326    va_end(ap);
 327    return true;
 328}
 329
 330/*
 331 * Like warn_report(), except print just once.
 332 * If *printed is false, print the message, and flip *printed to true.
 333 * Return whether the message was printed.
 334 */
 335bool warn_report_once_cond(bool *printed, const char *fmt, ...)
 336{
 337    va_list ap;
 338
 339    assert(printed);
 340    if (*printed) {
 341        return false;
 342    }
 343    *printed = true;
 344    va_start(ap, fmt);
 345    vreport(REPORT_TYPE_WARNING, fmt, ap);
 346    va_end(ap);
 347    return true;
 348}
 349
 350static char *qemu_glog_domains;
 351
 352static void qemu_log_func(const gchar *log_domain,
 353                          GLogLevelFlags log_level,
 354                          const gchar *message,
 355                          gpointer user_data)
 356{
 357    switch (log_level & G_LOG_LEVEL_MASK) {
 358    case G_LOG_LEVEL_DEBUG:
 359    case G_LOG_LEVEL_INFO:
 360        /*
 361         * Use same G_MESSAGES_DEBUG logic as glib to enable/disable debug
 362         * messages
 363         */
 364        if (qemu_glog_domains == NULL) {
 365            break;
 366        }
 367        if (strcmp(qemu_glog_domains, "all") != 0 &&
 368          (log_domain == NULL || !strstr(qemu_glog_domains, log_domain))) {
 369            break;
 370        }
 371        /* Fall through */
 372    case G_LOG_LEVEL_MESSAGE:
 373        info_report("%s%s%s",
 374                    log_domain ?: "", log_domain ? ": " : "", message);
 375
 376        break;
 377    case G_LOG_LEVEL_WARNING:
 378        warn_report("%s%s%s",
 379                    log_domain ?: "", log_domain ? ": " : "", message);
 380        break;
 381    case G_LOG_LEVEL_CRITICAL:
 382    case G_LOG_LEVEL_ERROR:
 383        error_report("%s%s%s",
 384                     log_domain ?: "", log_domain ? ": " : "", message);
 385        break;
 386    }
 387}
 388
 389void error_init(const char *argv0)
 390{
 391    const char *p = strrchr(argv0, '/');
 392
 393    /* Set the program name for error_print_loc(). */
 394    g_set_prgname(p ? p + 1 : argv0);
 395
 396    /*
 397     * This sets up glib logging so libraries using it also print their logs
 398     * through error_report(), warn_report(), info_report().
 399     */
 400    g_log_set_default_handler(qemu_log_func, NULL);
 401    g_warn_if_fail(qemu_glog_domains == NULL);
 402    qemu_glog_domains = g_strdup(g_getenv("G_MESSAGES_DEBUG"));
 403}
 404