]>
Commit | Line | Data |
---|---|---|
5726c27f BS |
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 | |
61f3c91a | 9 | * version 2.1 of the License, or (at your option) any later version. |
5726c27f BS |
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 | ||
d38ea87a | 20 | #include "qemu/osdep.h" |
1de7afc9 | 21 | #include "qemu/log.h" |
3514552e AB |
22 | #include "qemu/range.h" |
23 | #include "qemu/error-report.h" | |
bd6fee9f | 24 | #include "qapi/error.h" |
3514552e | 25 | #include "qemu/cutils.h" |
c84ea00d | 26 | #include "trace/control.h" |
b8121fe7 | 27 | #include "qemu/thread.h" |
6e8a355d | 28 | #include "qemu/lockable.h" |
7fc493f8 | 29 | #include "qemu/rcu.h" |
4e51069d RH |
30 | #ifdef CONFIG_LINUX |
31 | #include <sys/syscall.h> | |
32 | #endif | |
7fc493f8 RH |
33 | |
34 | ||
d5f55fff | 35 | typedef struct RCUCloseFILE { |
7fc493f8 RH |
36 | struct rcu_head rcu; |
37 | FILE *fd; | |
d5f55fff | 38 | } RCUCloseFILE; |
5726c27f | 39 | |
702979f7 RH |
40 | /* Mutex covering the other global_* variables. */ |
41 | static QemuMutex global_mutex; | |
42266464 | 42 | static char *global_filename; |
30f5a73a | 43 | static FILE *global_file; |
4e51069d | 44 | static __thread FILE *thread_file; |
eff3de52 | 45 | static __thread Notifier qemu_log_thread_cleanup_notifier; |
702979f7 | 46 | |
eeacee4d | 47 | int qemu_loglevel; |
4e51069d | 48 | static bool log_per_thread; |
3514552e | 49 | static GArray *debug_regions; |
5726c27f | 50 | |
7fc493f8 RH |
51 | /* Returns true if qemu_log() will really write somewhere. */ |
52 | bool qemu_log_enabled(void) | |
53 | { | |
4e51069d | 54 | return log_per_thread || qatomic_read(&global_file) != NULL; |
7fc493f8 RH |
55 | } |
56 | ||
57 | /* Returns true if qemu_log() will write somewhere other than stderr. */ | |
58 | bool qemu_log_separate(void) | |
59 | { | |
4e51069d RH |
60 | if (log_per_thread) { |
61 | return true; | |
62 | } else { | |
63 | FILE *logfile = qatomic_read(&global_file); | |
64 | return logfile && logfile != stderr; | |
65 | } | |
66 | } | |
67 | ||
68 | static int log_thread_id(void) | |
69 | { | |
70 | #ifdef CONFIG_GETTID | |
71 | return gettid(); | |
72 | #elif defined(SYS_gettid) | |
73 | return syscall(SYS_gettid); | |
74 | #else | |
75 | static int counter; | |
76 | return qatomic_fetch_inc(&counter); | |
77 | #endif | |
7fc493f8 RH |
78 | } |
79 | ||
eff3de52 GK |
80 | static void qemu_log_thread_cleanup(Notifier *n, void *unused) |
81 | { | |
9b063b7e GK |
82 | if (thread_file != stderr) { |
83 | fclose(thread_file); | |
84 | thread_file = NULL; | |
85 | } | |
eff3de52 GK |
86 | } |
87 | ||
c59fe6e5 RH |
88 | /* Lock/unlock output. */ |
89 | ||
9b063b7e | 90 | static FILE *qemu_log_trylock_with_err(Error **errp) |
c59fe6e5 | 91 | { |
30f5a73a | 92 | FILE *logfile; |
c60f599b | 93 | |
4e51069d RH |
94 | logfile = thread_file; |
95 | if (!logfile) { | |
96 | if (log_per_thread) { | |
97 | g_autofree char *filename | |
98 | = g_strdup_printf(global_filename, log_thread_id()); | |
99 | logfile = fopen(filename, "w"); | |
100 | if (!logfile) { | |
9b063b7e GK |
101 | error_setg_errno(errp, errno, |
102 | "Error opening logfile %s for thread %d", | |
103 | filename, log_thread_id()); | |
4e51069d RH |
104 | return NULL; |
105 | } | |
106 | thread_file = logfile; | |
eff3de52 GK |
107 | qemu_log_thread_cleanup_notifier.notify = qemu_log_thread_cleanup; |
108 | qemu_thread_atexit_add(&qemu_log_thread_cleanup_notifier); | |
4e51069d RH |
109 | } else { |
110 | rcu_read_lock(); | |
111 | /* | |
112 | * FIXME: typeof_strip_qual, as used by qatomic_rcu_read, | |
113 | * does not work with pointers to undefined structures, | |
114 | * such as we have with struct _IO_FILE and musl libc. | |
115 | * Since all we want is a read of a pointer, cast to void**, | |
116 | * which does work with typeof_strip_qual. | |
117 | */ | |
118 | logfile = qatomic_rcu_read((void **)&global_file); | |
119 | if (!logfile) { | |
120 | rcu_read_unlock(); | |
121 | return NULL; | |
122 | } | |
123 | } | |
c59fe6e5 | 124 | } |
4e51069d RH |
125 | |
126 | qemu_flockfile(logfile); | |
30f5a73a | 127 | return logfile; |
c59fe6e5 RH |
128 | } |
129 | ||
9b063b7e GK |
130 | FILE *qemu_log_trylock(void) |
131 | { | |
132 | return qemu_log_trylock_with_err(NULL); | |
133 | } | |
134 | ||
30f5a73a | 135 | void qemu_log_unlock(FILE *logfile) |
c59fe6e5 | 136 | { |
30f5a73a RH |
137 | if (logfile) { |
138 | fflush(logfile); | |
139 | qemu_funlockfile(logfile); | |
4e51069d RH |
140 | if (!log_per_thread) { |
141 | rcu_read_unlock(); | |
142 | } | |
c59fe6e5 | 143 | } |
c59fe6e5 RH |
144 | } |
145 | ||
3c06a417 | 146 | void qemu_log(const char *fmt, ...) |
eeacee4d | 147 | { |
095e9855 | 148 | FILE *f = qemu_log_trylock(); |
095e9855 | 149 | if (f) { |
bdfb460e | 150 | va_list ap; |
095e9855 | 151 | |
bdfb460e | 152 | va_start(ap, fmt); |
3c06a417 | 153 | vfprintf(f, fmt, ap); |
bdfb460e | 154 | va_end(ap); |
095e9855 | 155 | qemu_log_unlock(f); |
eeacee4d | 156 | } |
eeacee4d BS |
157 | } |
158 | ||
702979f7 | 159 | static void __attribute__((__constructor__)) startup(void) |
b8121fe7 | 160 | { |
702979f7 | 161 | qemu_mutex_init(&global_mutex); |
b8121fe7 RF |
162 | } |
163 | ||
d5f55fff | 164 | static void rcu_close_file(RCUCloseFILE *r) |
7606488c | 165 | { |
30f5a73a | 166 | fclose(r->fd); |
d5f55fff | 167 | g_free(r); |
7606488c RF |
168 | } |
169 | ||
4e51069d RH |
170 | /** |
171 | * valid_filename_template: | |
172 | * | |
173 | * Validate the filename template. Require %d if per_thread, allow it | |
174 | * otherwise; require no other % within the template. | |
175 | */ | |
176 | ||
177 | typedef enum { | |
178 | vft_error, | |
179 | vft_stderr, | |
180 | vft_strdup, | |
181 | vft_pid_printf, | |
182 | } ValidFilenameTemplateResult; | |
183 | ||
184 | static ValidFilenameTemplateResult | |
185 | valid_filename_template(const char *filename, bool per_thread, Error **errp) | |
186 | { | |
187 | if (filename) { | |
188 | char *pidstr = strstr(filename, "%"); | |
189 | ||
190 | if (pidstr) { | |
191 | /* We only accept one %d, no other format strings */ | |
192 | if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) { | |
193 | error_setg(errp, "Bad logfile template: %s", filename); | |
194 | return 0; | |
195 | } | |
196 | return per_thread ? vft_strdup : vft_pid_printf; | |
197 | } | |
198 | } | |
199 | if (per_thread) { | |
200 | error_setg(errp, "Filename template with '%%d' required for 'tid'"); | |
201 | return vft_error; | |
202 | } | |
203 | return filename ? vft_strdup : vft_stderr; | |
204 | } | |
205 | ||
5726c27f | 206 | /* enable or disable low levels log */ |
144539d3 RH |
207 | static bool qemu_set_log_internal(const char *filename, bool changed_name, |
208 | int log_flags, Error **errp) | |
5726c27f | 209 | { |
144539d3 | 210 | bool need_to_open_file; |
beab3447 | 211 | bool daemonized; |
4e51069d | 212 | bool per_thread; |
30f5a73a | 213 | FILE *logfile; |
7606488c | 214 | |
702979f7 | 215 | QEMU_LOCK_GUARD(&global_mutex); |
8ae58d60 | 216 | logfile = global_file; |
479b350e GK |
217 | |
218 | /* The per-thread flag is immutable. */ | |
219 | if (log_per_thread) { | |
220 | log_flags |= LOG_PER_THREAD; | |
524fc737 GK |
221 | } else { |
222 | if (global_filename) { | |
223 | log_flags &= ~LOG_PER_THREAD; | |
224 | } | |
479b350e | 225 | } |
144539d3 | 226 | |
4e51069d RH |
227 | per_thread = log_flags & LOG_PER_THREAD; |
228 | ||
144539d3 RH |
229 | if (changed_name) { |
230 | char *newname = NULL; | |
231 | ||
232 | /* | |
4e51069d RH |
233 | * Once threads start opening their own log files, we have no |
234 | * easy mechanism to tell them all to close and re-open. | |
235 | * There seems little cause to do so either -- this option | |
236 | * will most often be used at user-only startup. | |
144539d3 | 237 | */ |
4e51069d RH |
238 | if (log_per_thread) { |
239 | error_setg(errp, "Cannot change log filename after setting 'tid'"); | |
240 | return false; | |
241 | } | |
242 | ||
243 | switch (valid_filename_template(filename, per_thread, errp)) { | |
244 | case vft_error: | |
245 | return false; | |
246 | case vft_stderr: | |
247 | break; | |
248 | case vft_strdup: | |
249 | newname = g_strdup(filename); | |
250 | break; | |
251 | case vft_pid_printf: | |
252 | newname = g_strdup_printf(filename, getpid()); | |
253 | break; | |
144539d3 RH |
254 | } |
255 | ||
42266464 RH |
256 | g_free(global_filename); |
257 | global_filename = newname; | |
144539d3 | 258 | filename = newname; |
144539d3 | 259 | } else { |
42266464 | 260 | filename = global_filename; |
4e51069d RH |
261 | if (per_thread && |
262 | valid_filename_template(filename, true, errp) == vft_error) { | |
263 | return false; | |
264 | } | |
144539d3 RH |
265 | } |
266 | ||
4e51069d RH |
267 | /* Once the per-thread flag is set, it cannot be unset. */ |
268 | if (per_thread) { | |
269 | log_per_thread = true; | |
270 | } | |
271 | /* The flag itself is not relevant for need_to_open_file. */ | |
272 | log_flags &= ~LOG_PER_THREAD; | |
ed7f5f1d | 273 | #ifdef CONFIG_TRACE_LOG |
144539d3 | 274 | log_flags |= LOG_TRACE; |
ed7f5f1d | 275 | #endif |
144539d3 RH |
276 | qemu_loglevel = log_flags; |
277 | ||
beab3447 | 278 | daemonized = is_daemonized(); |
9b063b7e GK |
279 | need_to_open_file = false; |
280 | if (!daemonized) { | |
281 | /* | |
282 | * If not daemonized we only log if qemu_loglevel is set, either to | |
283 | * stderr or to a file (if there is a filename). | |
284 | * If per-thread, open the file for each thread in qemu_log_trylock(). | |
285 | */ | |
286 | need_to_open_file = qemu_loglevel && !log_per_thread; | |
287 | } else { | |
288 | /* | |
289 | * If we are daemonized, we will only log if there is a filename. | |
290 | */ | |
291 | need_to_open_file = filename != NULL; | |
292 | } | |
144539d3 | 293 | |
59bde213 PB |
294 | if (logfile) { |
295 | fflush(logfile); | |
296 | if (changed_name && logfile != stderr) { | |
30f5a73a RH |
297 | RCUCloseFILE *r = g_new0(RCUCloseFILE, 1); |
298 | r->fd = logfile; | |
59bde213 | 299 | qatomic_rcu_set(&global_file, NULL); |
30f5a73a | 300 | call_rcu(r, rcu_close_file, rcu); |
f05142d5 FE |
301 | } |
302 | if (changed_name) { | |
59bde213 | 303 | logfile = NULL; |
30f5a73a | 304 | } |
144539d3 | 305 | } |
92b24cb7 | 306 | |
9b063b7e GK |
307 | if (log_per_thread && daemonized) { |
308 | logfile = thread_file; | |
309 | } | |
310 | ||
144539d3 | 311 | if (!logfile && need_to_open_file) { |
144539d3 | 312 | if (filename) { |
9b063b7e GK |
313 | if (log_per_thread) { |
314 | logfile = qemu_log_trylock_with_err(errp); | |
315 | if (!logfile) { | |
316 | return false; | |
317 | } | |
318 | qemu_log_unlock(logfile); | |
319 | } else { | |
320 | logfile = fopen(filename, "w"); | |
321 | if (!logfile) { | |
322 | error_setg_errno(errp, errno, "Error opening logfile %s", | |
323 | filename); | |
324 | return false; | |
325 | } | |
989b697d | 326 | } |
96c33a45 | 327 | /* In case we are a daemon redirect stderr to logfile */ |
beab3447 | 328 | if (daemonized) { |
30f5a73a RH |
329 | dup2(fileno(logfile), STDERR_FILENO); |
330 | fclose(logfile); | |
9b063b7e GK |
331 | /* |
332 | * This will skip closing logfile in rcu_close_file() | |
333 | * or qemu_log_thread_cleanup(). | |
334 | */ | |
30f5a73a | 335 | logfile = stderr; |
96c33a45 | 336 | } |
989b697d PM |
337 | } else { |
338 | /* Default to stderr if no log file specified */ | |
beab3447 | 339 | assert(!daemonized); |
30f5a73a | 340 | logfile = stderr; |
5726c27f | 341 | } |
3437e545 | 342 | |
9b063b7e GK |
343 | if (log_per_thread && daemonized) { |
344 | thread_file = logfile; | |
345 | } else { | |
346 | qatomic_rcu_set(&global_file, logfile); | |
347 | } | |
5726c27f | 348 | } |
c5955f4f | 349 | return true; |
5726c27f | 350 | } |
f2937a33 | 351 | |
144539d3 | 352 | bool qemu_set_log(int log_flags, Error **errp) |
5726c27f | 353 | { |
144539d3 RH |
354 | return qemu_set_log_internal(NULL, false, log_flags, errp); |
355 | } | |
f6880b7f | 356 | |
144539d3 RH |
357 | bool qemu_set_log_filename(const char *filename, Error **errp) |
358 | { | |
359 | return qemu_set_log_internal(filename, true, qemu_loglevel, errp); | |
360 | } | |
e144a605 | 361 | |
144539d3 RH |
362 | bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp) |
363 | { | |
364 | return qemu_set_log_internal(name, true, flags, errp); | |
5726c27f BS |
365 | } |
366 | ||
3514552e AB |
367 | /* Returns true if addr is in our debug filter or no filter defined |
368 | */ | |
369 | bool qemu_log_in_addr_range(uint64_t addr) | |
370 | { | |
371 | if (debug_regions) { | |
372 | int i = 0; | |
373 | for (i = 0; i < debug_regions->len; i++) { | |
58e19e6e | 374 | Range *range = &g_array_index(debug_regions, Range, i); |
a0efbf16 | 375 | if (range_contains(range, addr)) { |
3514552e AB |
376 | return true; |
377 | } | |
378 | } | |
379 | return false; | |
380 | } else { | |
381 | return true; | |
382 | } | |
383 | } | |
384 | ||
385 | ||
bd6fee9f | 386 | void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp) |
3514552e AB |
387 | { |
388 | gchar **ranges = g_strsplit(filter_spec, ",", 0); | |
bd6fee9f | 389 | int i; |
2ec62fae MA |
390 | |
391 | if (debug_regions) { | |
392 | g_array_unref(debug_regions); | |
393 | debug_regions = NULL; | |
394 | } | |
395 | ||
bd6fee9f MA |
396 | debug_regions = g_array_sized_new(FALSE, FALSE, |
397 | sizeof(Range), g_strv_length(ranges)); | |
398 | for (i = 0; ranges[i]; i++) { | |
399 | const char *r = ranges[i]; | |
400 | const char *range_op, *r2, *e; | |
58e19e6e | 401 | uint64_t r1val, r2val, lob, upb; |
bd6fee9f MA |
402 | struct Range range; |
403 | ||
404 | range_op = strstr(r, "-"); | |
405 | r2 = range_op ? range_op + 1 : NULL; | |
406 | if (!range_op) { | |
407 | range_op = strstr(r, "+"); | |
408 | r2 = range_op ? range_op + 1 : NULL; | |
409 | } | |
410 | if (!range_op) { | |
411 | range_op = strstr(r, ".."); | |
412 | r2 = range_op ? range_op + 2 : NULL; | |
413 | } | |
414 | if (!range_op) { | |
415 | error_setg(errp, "Bad range specifier"); | |
416 | goto out; | |
417 | } | |
418 | ||
b30d1886 | 419 | if (qemu_strtou64(r, &e, 0, &r1val) |
bd6fee9f MA |
420 | || e != range_op) { |
421 | error_setg(errp, "Invalid number to the left of %.*s", | |
422 | (int)(r2 - range_op), range_op); | |
423 | goto out; | |
424 | } | |
b30d1886 | 425 | if (qemu_strtou64(r2, NULL, 0, &r2val)) { |
bd6fee9f MA |
426 | error_setg(errp, "Invalid number to the right of %.*s", |
427 | (int)(r2 - range_op), range_op); | |
428 | goto out; | |
429 | } | |
bd6fee9f MA |
430 | |
431 | switch (*range_op) { | |
432 | case '+': | |
58e19e6e MA |
433 | lob = r1val; |
434 | upb = r1val + r2val - 1; | |
bd6fee9f MA |
435 | break; |
436 | case '-': | |
58e19e6e MA |
437 | upb = r1val; |
438 | lob = r1val - (r2val - 1); | |
bd6fee9f MA |
439 | break; |
440 | case '.': | |
58e19e6e MA |
441 | lob = r1val; |
442 | upb = r2val; | |
bd6fee9f MA |
443 | break; |
444 | default: | |
445 | g_assert_not_reached(); | |
3514552e | 446 | } |
58eeb83c | 447 | if (lob > upb) { |
58e19e6e MA |
448 | error_setg(errp, "Invalid range"); |
449 | goto out; | |
450 | } | |
a0efbf16 | 451 | range_set_bounds(&range, lob, upb); |
bd6fee9f | 452 | g_array_append_val(debug_regions, range); |
3514552e | 453 | } |
bd6fee9f MA |
454 | out: |
455 | g_strfreev(ranges); | |
3514552e AB |
456 | } |
457 | ||
38dad9e5 | 458 | const QEMULogItem qemu_log_items[] = { |
5726c27f BS |
459 | { CPU_LOG_TB_OUT_ASM, "out_asm", |
460 | "show generated host assembly code for each compiled TB" }, | |
461 | { CPU_LOG_TB_IN_ASM, "in_asm", | |
462 | "show target assembly code for each compiled TB" }, | |
463 | { CPU_LOG_TB_OP, "op", | |
464 | "show micro ops for each compiled TB" }, | |
465 | { CPU_LOG_TB_OP_OPT, "op_opt", | |
5a18407f RH |
466 | "show micro ops after optimization" }, |
467 | { CPU_LOG_TB_OP_IND, "op_ind", | |
468 | "show micro ops before indirect lowering" }, | |
b384c734 RH |
469 | #ifdef CONFIG_PLUGIN |
470 | { LOG_TB_OP_PLUGIN, "op_plugin", | |
471 | "show micro ops before plugin injection" }, | |
472 | #endif | |
5726c27f BS |
473 | { CPU_LOG_INT, "int", |
474 | "show interrupts/exceptions in short format" }, | |
475 | { CPU_LOG_EXEC, "exec", | |
476 | "show trace before each executed TB (lots of logs)" }, | |
477 | { CPU_LOG_TB_CPU, "cpu", | |
54195736 | 478 | "show CPU registers before entering a TB (lots of logs)" }, |
ae765180 PM |
479 | { CPU_LOG_TB_FPU, "fpu", |
480 | "include FPU registers in the 'cpu' logging" }, | |
339aaf5b AP |
481 | { CPU_LOG_MMU, "mmu", |
482 | "log MMU-related activities" }, | |
5726c27f | 483 | { CPU_LOG_PCALL, "pcall", |
3437e545 | 484 | "x86 only: show protected mode far calls/returns/exceptions" }, |
5726c27f | 485 | { CPU_LOG_RESET, "cpu_reset", |
dbfe1b6a | 486 | "show CPU state before CPU resets" }, |
dafdf1ab BS |
487 | { LOG_UNIMP, "unimp", |
488 | "log unimplemented functionality" }, | |
e54eba19 PM |
489 | { LOG_GUEST_ERROR, "guest_errors", |
490 | "log when the guest OS does something invalid (eg accessing a\n" | |
491 | "non-existent register)" }, | |
13829020 PB |
492 | { CPU_LOG_PAGE, "page", |
493 | "dump pages at beginning of user mode emulation" }, | |
89a82cd4 RH |
494 | { CPU_LOG_TB_NOCHAIN, "nochain", |
495 | "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n" | |
496 | "complete traces" }, | |
ca76a669 | 497 | #ifdef CONFIG_PLUGIN |
cb9291e5 | 498 | { CPU_LOG_PLUGIN, "plugin", "output from TCG plugins"}, |
ca76a669 | 499 | #endif |
4b25a506 JK |
500 | { LOG_STRACE, "strace", |
501 | "log every user-mode syscall, its input, and its result" }, | |
4e51069d RH |
502 | { LOG_PER_THREAD, "tid", |
503 | "open a separate log file per thread; filename must contain '%d'" }, | |
b84694de IK |
504 | { CPU_LOG_TB_VPU, "vpu", |
505 | "include VPU registers in the 'cpu' logging" }, | |
5726c27f BS |
506 | { 0, NULL, NULL }, |
507 | }; | |
508 | ||
5726c27f | 509 | /* takes a comma separated list of log masks. Return 0 if error. */ |
4fde1eba | 510 | int qemu_str_to_log_mask(const char *str) |
5726c27f | 511 | { |
38dad9e5 | 512 | const QEMULogItem *item; |
89d0a64f DB |
513 | int mask = 0; |
514 | char **parts = g_strsplit(str, ",", 0); | |
515 | char **tmp; | |
5726c27f | 516 | |
89d0a64f DB |
517 | for (tmp = parts; tmp && *tmp; tmp++) { |
518 | if (g_str_equal(*tmp, "all")) { | |
38dad9e5 | 519 | for (item = qemu_log_items; item->mask != 0; item++) { |
5726c27f BS |
520 | mask |= item->mask; |
521 | } | |
c84ea00d | 522 | #ifdef CONFIG_TRACE_LOG |
89d0a64f DB |
523 | } else if (g_str_has_prefix(*tmp, "trace:") && (*tmp)[6] != '\0') { |
524 | trace_enable_events((*tmp) + 6); | |
c84ea00d PB |
525 | mask |= LOG_TRACE; |
526 | #endif | |
5726c27f | 527 | } else { |
38dad9e5 | 528 | for (item = qemu_log_items; item->mask != 0; item++) { |
89d0a64f | 529 | if (g_str_equal(*tmp, item->name)) { |
5726c27f BS |
530 | goto found; |
531 | } | |
532 | } | |
89d0a64f | 533 | goto error; |
c84ea00d PB |
534 | found: |
535 | mask |= item->mask; | |
5726c27f | 536 | } |
5726c27f | 537 | } |
89d0a64f DB |
538 | |
539 | g_strfreev(parts); | |
5726c27f | 540 | return mask; |
89d0a64f DB |
541 | |
542 | error: | |
543 | g_strfreev(parts); | |
544 | return 0; | |
5726c27f | 545 | } |
59a6fa6e PM |
546 | |
547 | void qemu_print_log_usage(FILE *f) | |
548 | { | |
38dad9e5 | 549 | const QEMULogItem *item; |
59a6fa6e | 550 | fprintf(f, "Log items (comma separated):\n"); |
38dad9e5 | 551 | for (item = qemu_log_items; item->mask != 0; item++) { |
c84ea00d | 552 | fprintf(f, "%-15s %s\n", item->name, item->help); |
59a6fa6e | 553 | } |
c84ea00d PB |
554 | #ifdef CONFIG_TRACE_LOG |
555 | fprintf(f, "trace:PATTERN enable trace events\n"); | |
556 | fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n"); | |
557 | #endif | |
59a6fa6e | 558 | } |