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