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 | 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 | |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
149 | static void error_set_progname(const char *argv0) |
150 | { |
151 | const char *p = strrchr(argv0, '/'); |
152 | progname = p ? p + 1 : argv0; |
153 | } |
154 | |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | */ |
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 | |
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 | |
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 | |