xref: /qemu/util/error-report.c (revision 49f95221)
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