qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

error-report.c (10085B)


      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  */
     21 typedef enum {
     22     REPORT_TYPE_ERROR,
     23     REPORT_TYPE_WARNING,
     24     REPORT_TYPE_INFO,
     25 } report_type;
     26 
     27 /* Prepend timestamp to messages */
     28 bool message_with_timestamp;
     29 bool error_with_guestname;
     30 const char *error_guest_name;
     31 
     32 int 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 
     43 static Location std_loc = {
     44     .kind = LOC_NONE
     45 };
     46 static 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  */
     53 Location *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  */
     67 Location *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  */
     78 Location *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  */
     89 Location *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  */
     99 void 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  */
    110 void 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  */
    118 void 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  */
    128 void 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  */
    141 static 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 
    172 static char *
    173 real_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  */
    196 static 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  */
    235 void 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  */
    246 void 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  */
    258 void 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  */
    270 void 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  */
    285 void 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  */
    301 void 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  */
    315 bool 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  */
    335 bool 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 
    350 static char *qemu_glog_domains;
    351 
    352 static 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 
    389 void 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 }