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