xref: /openbmc/qemu/util/error-report.c (revision 89fc45d5)
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     return g_date_time_format_iso8601(dt);
189 #else
190     GTimeVal tv;
191     g_get_current_time(&tv);
192     return g_time_val_to_iso8601(&tv);
193 #endif
194 }
195 
196 /*
197  * Print a message to current monitor if we have one, else to stderr.
198  * @report_type is the type of message: error, warning or informational.
199  * Format arguments like vsprintf().  The resulting message should be
200  * a single phrase, with no newline or trailing punctuation.
201  * Prepend the current location and append a newline.
202  */
203 static void vreport(report_type type, const char *fmt, va_list ap)
204 {
205     gchar *timestr;
206 
207     if (message_with_timestamp && !monitor_cur()) {
208         timestr = real_time_iso8601();
209         error_printf("%s ", timestr);
210         g_free(timestr);
211     }
212 
213     /* Only prepend guest name if -msg guest-name and -name guest=... are set */
214     if (error_with_guestname && error_guest_name && !monitor_cur()) {
215         error_printf("%s ", error_guest_name);
216     }
217 
218     print_loc();
219 
220     switch (type) {
221     case REPORT_TYPE_ERROR:
222         break;
223     case REPORT_TYPE_WARNING:
224         error_printf("warning: ");
225         break;
226     case REPORT_TYPE_INFO:
227         error_printf("info: ");
228         break;
229     }
230 
231     error_vprintf(fmt, ap);
232     error_printf("\n");
233 }
234 
235 /*
236  * Print an error message to current monitor if we have one, else to stderr.
237  * Format arguments like vsprintf().  The resulting message should be
238  * a single phrase, with no newline or trailing punctuation.
239  * Prepend the current location and append a newline.
240  * It's wrong to call this in a QMP monitor.  Use error_setg() there.
241  */
242 void error_vreport(const char *fmt, va_list ap)
243 {
244     vreport(REPORT_TYPE_ERROR, fmt, ap);
245 }
246 
247 /*
248  * Print a warning message to current monitor if we have one, else to stderr.
249  * Format arguments like vsprintf().  The resulting message should be
250  * a single phrase, with no newline or trailing punctuation.
251  * Prepend the current location and append a newline.
252  */
253 void warn_vreport(const char *fmt, va_list ap)
254 {
255     vreport(REPORT_TYPE_WARNING, fmt, ap);
256 }
257 
258 /*
259  * Print an information message to current monitor if we have one, else to
260  * stderr.
261  * Format arguments like vsprintf().  The resulting message should be
262  * a single phrase, with no newline or trailing punctuation.
263  * Prepend the current location and append a newline.
264  */
265 void info_vreport(const char *fmt, va_list ap)
266 {
267     vreport(REPORT_TYPE_INFO, fmt, ap);
268 }
269 
270 /*
271  * Print an error message to current monitor if we have one, else to stderr.
272  * Format arguments like sprintf().  The resulting message should be
273  * a single phrase, with no newline or trailing punctuation.
274  * Prepend the current location and append a newline.
275  * It's wrong to call this in a QMP monitor.  Use error_setg() there.
276  */
277 void error_report(const char *fmt, ...)
278 {
279     va_list ap;
280 
281     va_start(ap, fmt);
282     vreport(REPORT_TYPE_ERROR, fmt, ap);
283     va_end(ap);
284 }
285 
286 /*
287  * Print a warning message to current monitor if we have one, else to stderr.
288  * Format arguments like sprintf(). The resulting message should be a
289  * single phrase, with no newline or trailing punctuation.
290  * Prepend the current location and append a newline.
291  */
292 void warn_report(const char *fmt, ...)
293 {
294     va_list ap;
295 
296     va_start(ap, fmt);
297     vreport(REPORT_TYPE_WARNING, fmt, ap);
298     va_end(ap);
299 }
300 
301 /*
302  * Print an information message to current monitor if we have one, else to
303  * stderr.
304  * Format arguments like sprintf(). The resulting message should be a
305  * single phrase, with no newline or trailing punctuation.
306  * Prepend the current location and append a newline.
307  */
308 void info_report(const char *fmt, ...)
309 {
310     va_list ap;
311 
312     va_start(ap, fmt);
313     vreport(REPORT_TYPE_INFO, fmt, ap);
314     va_end(ap);
315 }
316 
317 /*
318  * Like error_report(), except print just once.
319  * If *printed is false, print the message, and flip *printed to true.
320  * Return whether the message was printed.
321  */
322 bool error_report_once_cond(bool *printed, const char *fmt, ...)
323 {
324     va_list ap;
325 
326     assert(printed);
327     if (*printed) {
328         return false;
329     }
330     *printed = true;
331     va_start(ap, fmt);
332     vreport(REPORT_TYPE_ERROR, fmt, ap);
333     va_end(ap);
334     return true;
335 }
336 
337 /*
338  * Like warn_report(), except print just once.
339  * If *printed is false, print the message, and flip *printed to true.
340  * Return whether the message was printed.
341  */
342 bool warn_report_once_cond(bool *printed, const char *fmt, ...)
343 {
344     va_list ap;
345 
346     assert(printed);
347     if (*printed) {
348         return false;
349     }
350     *printed = true;
351     va_start(ap, fmt);
352     vreport(REPORT_TYPE_WARNING, fmt, ap);
353     va_end(ap);
354     return true;
355 }
356 
357 static char *qemu_glog_domains;
358 
359 static void qemu_log_func(const gchar *log_domain,
360                           GLogLevelFlags log_level,
361                           const gchar *message,
362                           gpointer user_data)
363 {
364     switch (log_level & G_LOG_LEVEL_MASK) {
365     case G_LOG_LEVEL_DEBUG:
366     case G_LOG_LEVEL_INFO:
367         /*
368          * Use same G_MESSAGES_DEBUG logic as glib to enable/disable debug
369          * messages
370          */
371         if (qemu_glog_domains == NULL) {
372             break;
373         }
374         if (strcmp(qemu_glog_domains, "all") != 0 &&
375           (log_domain == NULL || !strstr(qemu_glog_domains, log_domain))) {
376             break;
377         }
378         /* Fall through */
379     case G_LOG_LEVEL_MESSAGE:
380         info_report("%s%s%s",
381                     log_domain ?: "", log_domain ? ": " : "", message);
382 
383         break;
384     case G_LOG_LEVEL_WARNING:
385         warn_report("%s%s%s",
386                     log_domain ?: "", log_domain ? ": " : "", message);
387         break;
388     case G_LOG_LEVEL_CRITICAL:
389     case G_LOG_LEVEL_ERROR:
390         error_report("%s%s%s",
391                      log_domain ?: "", log_domain ? ": " : "", message);
392         break;
393     }
394 }
395 
396 void error_init(const char *argv0)
397 {
398     const char *p = strrchr(argv0, '/');
399 
400     /* Set the program name for error_print_loc(). */
401     g_set_prgname(p ? p + 1 : argv0);
402 
403     /*
404      * This sets up glib logging so libraries using it also print their logs
405      * through error_report(), warn_report(), info_report().
406      */
407     g_log_set_default_handler(qemu_log_func, NULL);
408     g_warn_if_fail(qemu_glog_domains == NULL);
409     qemu_glog_domains = g_strdup(g_getenv("G_MESSAGES_DEBUG"));
410 }
411