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