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 G_GNUC_PRINTF(2, 0) 197 static 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 */ 236 void 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 */ 247 void 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 */ 259 void 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 */ 271 void 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 */ 286 void 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 */ 302 void 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 */ 316 bool 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 */ 336 bool 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 351 static char *qemu_glog_domains; 352 353 static 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 390 void 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