xref: /openbmc/qemu/util/log.c (revision 87776ab7)
1 /*
2  * Logging support
3  *
4  *  Copyright (c) 2003 Fabrice Bellard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "qemu-common.h"
22 #include "qemu/log.h"
23 #include "qemu/range.h"
24 #include "qemu/error-report.h"
25 #include "qemu/cutils.h"
26 #include "trace/control.h"
27 
28 static char *logfilename;
29 FILE *qemu_logfile;
30 int qemu_loglevel;
31 static int log_append = 0;
32 static GArray *debug_regions;
33 
34 void qemu_log(const char *fmt, ...)
35 {
36     va_list ap;
37 
38     va_start(ap, fmt);
39     if (qemu_logfile) {
40         vfprintf(qemu_logfile, fmt, ap);
41     }
42     va_end(ap);
43 }
44 
45 static bool log_uses_own_buffers;
46 
47 /* enable or disable low levels log */
48 void qemu_set_log(int log_flags)
49 {
50     qemu_loglevel = log_flags;
51 #ifdef CONFIG_TRACE_LOG
52     qemu_loglevel |= LOG_TRACE;
53 #endif
54     if (!qemu_logfile &&
55         (is_daemonized() ? logfilename != NULL : qemu_loglevel)) {
56         if (logfilename) {
57             qemu_logfile = fopen(logfilename, log_append ? "a" : "w");
58             if (!qemu_logfile) {
59                 perror(logfilename);
60                 _exit(1);
61             }
62             /* In case we are a daemon redirect stderr to logfile */
63             if (is_daemonized()) {
64                 dup2(fileno(qemu_logfile), STDERR_FILENO);
65                 fclose(qemu_logfile);
66                 /* This will skip closing logfile in qemu_log_close() */
67                 qemu_logfile = stderr;
68             }
69         } else {
70             /* Default to stderr if no log file specified */
71             assert(!is_daemonized());
72             qemu_logfile = stderr;
73         }
74         /* must avoid mmap() usage of glibc by setting a buffer "by hand" */
75         if (log_uses_own_buffers) {
76             static char logfile_buf[4096];
77 
78             setvbuf(qemu_logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
79         } else {
80 #if defined(_WIN32)
81             /* Win32 doesn't support line-buffering, so use unbuffered output. */
82             setvbuf(qemu_logfile, NULL, _IONBF, 0);
83 #else
84             setvbuf(qemu_logfile, NULL, _IOLBF, 0);
85 #endif
86             log_append = 1;
87         }
88     }
89     if (qemu_logfile &&
90         (is_daemonized() ? logfilename == NULL : !qemu_loglevel)) {
91         qemu_log_close();
92     }
93 }
94 
95 void qemu_log_needs_buffers(void)
96 {
97     log_uses_own_buffers = true;
98 }
99 
100 /*
101  * Allow the user to include %d in their logfile which will be
102  * substituted with the current PID. This is useful for debugging many
103  * nested linux-user tasks but will result in lots of logs.
104  */
105 void qemu_set_log_filename(const char *filename)
106 {
107     char *pidstr;
108     g_free(logfilename);
109 
110     pidstr = strstr(filename, "%");
111     if (pidstr) {
112         /* We only accept one %d, no other format strings */
113         if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) {
114             error_report("Bad logfile format: %s", filename);
115             logfilename = NULL;
116         } else {
117             logfilename = g_strdup_printf(filename, getpid());
118         }
119     } else {
120         logfilename = g_strdup(filename);
121     }
122     qemu_log_close();
123     qemu_set_log(qemu_loglevel);
124 }
125 
126 /* Returns true if addr is in our debug filter or no filter defined
127  */
128 bool qemu_log_in_addr_range(uint64_t addr)
129 {
130     if (debug_regions) {
131         int i = 0;
132         for (i = 0; i < debug_regions->len; i++) {
133             struct Range *range = &g_array_index(debug_regions, Range, i);
134             if (addr >= range->begin && addr <= range->end) {
135                 return true;
136             }
137         }
138         return false;
139     } else {
140         return true;
141     }
142 }
143 
144 
145 void qemu_set_dfilter_ranges(const char *filter_spec)
146 {
147     gchar **ranges = g_strsplit(filter_spec, ",", 0);
148     if (ranges) {
149         gchar **next = ranges;
150         gchar *r = *next++;
151         debug_regions = g_array_sized_new(FALSE, FALSE,
152                                           sizeof(Range), g_strv_length(ranges));
153         while (r) {
154             char *range_op = strstr(r, "-");
155             char *r2 = range_op ? range_op + 1 : NULL;
156             if (!range_op) {
157                 range_op = strstr(r, "+");
158                 r2 = range_op ? range_op + 1 : NULL;
159             }
160             if (!range_op) {
161                 range_op = strstr(r, "..");
162                 r2 = range_op ? range_op + 2 : NULL;
163             }
164             if (range_op) {
165                 const char *e = NULL;
166                 uint64_t r1val, r2val;
167 
168                 if ((qemu_strtoull(r, &e, 0, &r1val) == 0) &&
169                     (qemu_strtoull(r2, NULL, 0, &r2val) == 0) &&
170                     r2val > 0) {
171                     struct Range range;
172 
173                     g_assert(e == range_op);
174 
175                     switch (*range_op) {
176                     case '+':
177                     {
178                         range.begin = r1val;
179                         range.end = r1val + (r2val - 1);
180                         break;
181                     }
182                     case '-':
183                     {
184                         range.end = r1val;
185                         range.begin = r1val - (r2val - 1);
186                         break;
187                     }
188                     case '.':
189                         range.begin = r1val;
190                         range.end = r2val;
191                         break;
192                     default:
193                         g_assert_not_reached();
194                     }
195                     g_array_append_val(debug_regions, range);
196 
197                 } else {
198                     g_error("Failed to parse range in: %s", r);
199                 }
200             } else {
201                 g_error("Bad range specifier in: %s", r);
202             }
203             r = *next++;
204         }
205         g_strfreev(ranges);
206     }
207 }
208 
209 /* fflush() the log file */
210 void qemu_log_flush(void)
211 {
212     fflush(qemu_logfile);
213 }
214 
215 /* Close the log file */
216 void qemu_log_close(void)
217 {
218     if (qemu_logfile) {
219         if (qemu_logfile != stderr) {
220             fclose(qemu_logfile);
221         }
222         qemu_logfile = NULL;
223     }
224 }
225 
226 const QEMULogItem qemu_log_items[] = {
227     { CPU_LOG_TB_OUT_ASM, "out_asm",
228       "show generated host assembly code for each compiled TB" },
229     { CPU_LOG_TB_IN_ASM, "in_asm",
230       "show target assembly code for each compiled TB" },
231     { CPU_LOG_TB_OP, "op",
232       "show micro ops for each compiled TB" },
233     { CPU_LOG_TB_OP_OPT, "op_opt",
234       "show micro ops (x86 only: before eflags optimization) and\n"
235       "after liveness analysis" },
236     { CPU_LOG_INT, "int",
237       "show interrupts/exceptions in short format" },
238     { CPU_LOG_EXEC, "exec",
239       "show trace before each executed TB (lots of logs)" },
240     { CPU_LOG_TB_CPU, "cpu",
241       "show CPU registers before entering a TB (lots of logs)" },
242     { CPU_LOG_MMU, "mmu",
243       "log MMU-related activities" },
244     { CPU_LOG_PCALL, "pcall",
245       "x86 only: show protected mode far calls/returns/exceptions" },
246     { CPU_LOG_RESET, "cpu_reset",
247       "show CPU state before CPU resets" },
248     { LOG_UNIMP, "unimp",
249       "log unimplemented functionality" },
250     { LOG_GUEST_ERROR, "guest_errors",
251       "log when the guest OS does something invalid (eg accessing a\n"
252       "non-existent register)" },
253     { CPU_LOG_PAGE, "page",
254       "dump pages at beginning of user mode emulation" },
255     { CPU_LOG_TB_NOCHAIN, "nochain",
256       "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n"
257       "complete traces" },
258     { 0, NULL, NULL },
259 };
260 
261 static int cmp1(const char *s1, int n, const char *s2)
262 {
263     if (strlen(s2) != n) {
264         return 0;
265     }
266     return memcmp(s1, s2, n) == 0;
267 }
268 
269 /* takes a comma separated list of log masks. Return 0 if error. */
270 int qemu_str_to_log_mask(const char *str)
271 {
272     const QEMULogItem *item;
273     int mask;
274     const char *p, *p1;
275 
276     p = str;
277     mask = 0;
278     for (;;) {
279         p1 = strchr(p, ',');
280         if (!p1) {
281             p1 = p + strlen(p);
282         }
283         if (cmp1(p,p1-p,"all")) {
284             for (item = qemu_log_items; item->mask != 0; item++) {
285                 mask |= item->mask;
286             }
287 #ifdef CONFIG_TRACE_LOG
288         } else if (strncmp(p, "trace:", 6) == 0 && p + 6 != p1) {
289             trace_enable_events(p + 6);
290             mask |= LOG_TRACE;
291 #endif
292         } else {
293             for (item = qemu_log_items; item->mask != 0; item++) {
294                 if (cmp1(p, p1 - p, item->name)) {
295                     goto found;
296                 }
297             }
298             return 0;
299         found:
300             mask |= item->mask;
301         }
302         if (*p1 != ',') {
303             break;
304         }
305         p = p1 + 1;
306     }
307     return mask;
308 }
309 
310 void qemu_print_log_usage(FILE *f)
311 {
312     const QEMULogItem *item;
313     fprintf(f, "Log items (comma separated):\n");
314     for (item = qemu_log_items; item->mask != 0; item++) {
315         fprintf(f, "%-15s %s\n", item->name, item->help);
316     }
317 #ifdef CONFIG_TRACE_LOG
318     fprintf(f, "trace:PATTERN   enable trace events\n");
319     fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n");
320 #endif
321 }
322