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