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