1d890d50dSDenis V. Lunev /*
2d890d50dSDenis V. Lunev * Logging support
3d890d50dSDenis V. Lunev *
4d890d50dSDenis V. Lunev * Copyright (c) 2003 Fabrice Bellard
5d890d50dSDenis V. Lunev *
6d890d50dSDenis V. Lunev * This library is free software; you can redistribute it and/or
7d890d50dSDenis V. Lunev * modify it under the terms of the GNU Lesser General Public
8d890d50dSDenis V. Lunev * License as published by the Free Software Foundation; either
961f3c91aSChetan Pant * version 2.1 of the License, or (at your option) any later version.
10d890d50dSDenis V. Lunev *
11d890d50dSDenis V. Lunev * This library is distributed in the hope that it will be useful,
12d890d50dSDenis V. Lunev * but WITHOUT ANY WARRANTY; without even the implied warranty of
13d890d50dSDenis V. Lunev * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14d890d50dSDenis V. Lunev * Lesser General Public License for more details.
15d890d50dSDenis V. Lunev *
16d890d50dSDenis V. Lunev * You should have received a copy of the GNU Lesser General Public
17d890d50dSDenis V. Lunev * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18d890d50dSDenis V. Lunev */
19d890d50dSDenis V. Lunev
20d38ea87aSPeter Maydell #include "qemu/osdep.h"
21d890d50dSDenis V. Lunev #include "qemu/log.h"
223514552eSAlex Bennée #include "qemu/range.h"
233514552eSAlex Bennée #include "qemu/error-report.h"
24bd6fee9fSMarkus Armbruster #include "qapi/error.h"
253514552eSAlex Bennée #include "qemu/cutils.h"
26c84ea00dSPaolo Bonzini #include "trace/control.h"
27b8121fe7SRobert Foley #include "qemu/thread.h"
286e8a355dSDaniel Brodsky #include "qemu/lockable.h"
297fc493f8SRichard Henderson #include "qemu/rcu.h"
304e51069dSRichard Henderson #ifdef CONFIG_LINUX
314e51069dSRichard Henderson #include <sys/syscall.h>
324e51069dSRichard Henderson #endif
337fc493f8SRichard Henderson
347fc493f8SRichard Henderson
35d5f55fffSRichard Henderson typedef struct RCUCloseFILE {
367fc493f8SRichard Henderson struct rcu_head rcu;
377fc493f8SRichard Henderson FILE *fd;
38d5f55fffSRichard Henderson } RCUCloseFILE;
39d890d50dSDenis V. Lunev
40702979f7SRichard Henderson /* Mutex covering the other global_* variables. */
41702979f7SRichard Henderson static QemuMutex global_mutex;
4242266464SRichard Henderson static char *global_filename;
4330f5a73aSRichard Henderson static FILE *global_file;
444e51069dSRichard Henderson static __thread FILE *thread_file;
45eff3de52SGreg Kurz static __thread Notifier qemu_log_thread_cleanup_notifier;
46702979f7SRichard Henderson
47d890d50dSDenis V. Lunev int qemu_loglevel;
484e51069dSRichard Henderson static bool log_per_thread;
493514552eSAlex Bennée static GArray *debug_regions;
50d890d50dSDenis V. Lunev
517fc493f8SRichard Henderson /* Returns true if qemu_log() will really write somewhere. */
qemu_log_enabled(void)527fc493f8SRichard Henderson bool qemu_log_enabled(void)
537fc493f8SRichard Henderson {
544e51069dSRichard Henderson return log_per_thread || qatomic_read(&global_file) != NULL;
557fc493f8SRichard Henderson }
567fc493f8SRichard Henderson
577fc493f8SRichard Henderson /* Returns true if qemu_log() will write somewhere other than stderr. */
qemu_log_separate(void)587fc493f8SRichard Henderson bool qemu_log_separate(void)
597fc493f8SRichard Henderson {
604e51069dSRichard Henderson if (log_per_thread) {
614e51069dSRichard Henderson return true;
624e51069dSRichard Henderson } else {
6330f5a73aSRichard Henderson FILE *logfile = qatomic_read(&global_file);
6430f5a73aSRichard Henderson return logfile && logfile != stderr;
657fc493f8SRichard Henderson }
664e51069dSRichard Henderson }
674e51069dSRichard Henderson
log_thread_id(void)684e51069dSRichard Henderson static int log_thread_id(void)
694e51069dSRichard Henderson {
704e51069dSRichard Henderson #ifdef CONFIG_GETTID
714e51069dSRichard Henderson return gettid();
724e51069dSRichard Henderson #elif defined(SYS_gettid)
734e51069dSRichard Henderson return syscall(SYS_gettid);
744e51069dSRichard Henderson #else
754e51069dSRichard Henderson static int counter;
764e51069dSRichard Henderson return qatomic_fetch_inc(&counter);
774e51069dSRichard Henderson #endif
784e51069dSRichard Henderson }
797fc493f8SRichard Henderson
qemu_log_thread_cleanup(Notifier * n,void * unused)80eff3de52SGreg Kurz static void qemu_log_thread_cleanup(Notifier *n, void *unused)
81eff3de52SGreg Kurz {
829b063b7eSGreg Kurz if (thread_file != stderr) {
83eff3de52SGreg Kurz fclose(thread_file);
84eff3de52SGreg Kurz thread_file = NULL;
85eff3de52SGreg Kurz }
869b063b7eSGreg Kurz }
87eff3de52SGreg Kurz
88c59fe6e5SRichard Henderson /* Lock/unlock output. */
89c59fe6e5SRichard Henderson
qemu_log_trylock_with_err(Error ** errp)909b063b7eSGreg Kurz static FILE *qemu_log_trylock_with_err(Error **errp)
91c59fe6e5SRichard Henderson {
9230f5a73aSRichard Henderson FILE *logfile;
93c60f599bSRichard Henderson
944e51069dSRichard Henderson logfile = thread_file;
954e51069dSRichard Henderson if (!logfile) {
964e51069dSRichard Henderson if (log_per_thread) {
974e51069dSRichard Henderson g_autofree char *filename
984e51069dSRichard Henderson = g_strdup_printf(global_filename, log_thread_id());
994e51069dSRichard Henderson logfile = fopen(filename, "w");
1004e51069dSRichard Henderson if (!logfile) {
1019b063b7eSGreg Kurz error_setg_errno(errp, errno,
1029b063b7eSGreg Kurz "Error opening logfile %s for thread %d",
1039b063b7eSGreg Kurz filename, log_thread_id());
1044e51069dSRichard Henderson return NULL;
1054e51069dSRichard Henderson }
1064e51069dSRichard Henderson thread_file = logfile;
107eff3de52SGreg Kurz qemu_log_thread_cleanup_notifier.notify = qemu_log_thread_cleanup;
108eff3de52SGreg Kurz qemu_thread_atexit_add(&qemu_log_thread_cleanup_notifier);
1094e51069dSRichard Henderson } else {
110c59fe6e5SRichard Henderson rcu_read_lock();
11130f5a73aSRichard Henderson /*
11230f5a73aSRichard Henderson * FIXME: typeof_strip_qual, as used by qatomic_rcu_read,
11330f5a73aSRichard Henderson * does not work with pointers to undefined structures,
11430f5a73aSRichard Henderson * such as we have with struct _IO_FILE and musl libc.
11530f5a73aSRichard Henderson * Since all we want is a read of a pointer, cast to void**,
11630f5a73aSRichard Henderson * which does work with typeof_strip_qual.
11730f5a73aSRichard Henderson */
11830f5a73aSRichard Henderson logfile = qatomic_rcu_read((void **)&global_file);
1194e51069dSRichard Henderson if (!logfile) {
120c60f599bSRichard Henderson rcu_read_unlock();
1214e51069dSRichard Henderson return NULL;
122c59fe6e5SRichard Henderson }
1234e51069dSRichard Henderson }
1244e51069dSRichard Henderson }
1254e51069dSRichard Henderson
1264e51069dSRichard Henderson qemu_flockfile(logfile);
12730f5a73aSRichard Henderson return logfile;
128c59fe6e5SRichard Henderson }
129c59fe6e5SRichard Henderson
qemu_log_trylock(void)1309b063b7eSGreg Kurz FILE *qemu_log_trylock(void)
1319b063b7eSGreg Kurz {
1329b063b7eSGreg Kurz return qemu_log_trylock_with_err(NULL);
1339b063b7eSGreg Kurz }
1349b063b7eSGreg Kurz
qemu_log_unlock(FILE * logfile)13530f5a73aSRichard Henderson void qemu_log_unlock(FILE *logfile)
136c59fe6e5SRichard Henderson {
13730f5a73aSRichard Henderson if (logfile) {
13830f5a73aSRichard Henderson fflush(logfile);
13930f5a73aSRichard Henderson qemu_funlockfile(logfile);
1404e51069dSRichard Henderson if (!log_per_thread) {
141c59fe6e5SRichard Henderson rcu_read_unlock();
142c59fe6e5SRichard Henderson }
143c60f599bSRichard Henderson }
1444e51069dSRichard Henderson }
145c59fe6e5SRichard Henderson
qemu_log(const char * fmt,...)1463c06a417SRichard Henderson void qemu_log(const char *fmt, ...)
147d890d50dSDenis V. Lunev {
148095e9855SRichard Henderson FILE *f = qemu_log_trylock();
149095e9855SRichard Henderson if (f) {
150bdfb460eSRichard Henderson va_list ap;
151095e9855SRichard Henderson
152bdfb460eSRichard Henderson va_start(ap, fmt);
1533c06a417SRichard Henderson vfprintf(f, fmt, ap);
154d890d50dSDenis V. Lunev va_end(ap);
155095e9855SRichard Henderson qemu_log_unlock(f);
156bdfb460eSRichard Henderson }
157bdfb460eSRichard Henderson }
158d890d50dSDenis V. Lunev
startup(void)159702979f7SRichard Henderson static void __attribute__((__constructor__)) startup(void)
160b8121fe7SRobert Foley {
161702979f7SRichard Henderson qemu_mutex_init(&global_mutex);
162b8121fe7SRobert Foley }
163b8121fe7SRobert Foley
rcu_close_file(RCUCloseFILE * r)164d5f55fffSRichard Henderson static void rcu_close_file(RCUCloseFILE *r)
1657606488cSRobert Foley {
166d5f55fffSRichard Henderson fclose(r->fd);
167d5f55fffSRichard Henderson g_free(r);
1687606488cSRobert Foley }
1697606488cSRobert Foley
1704e51069dSRichard Henderson /**
1714e51069dSRichard Henderson * valid_filename_template:
172e144a605SSalvador Fandino *
1734e51069dSRichard Henderson * Validate the filename template. Require %d if per_thread, allow it
1744e51069dSRichard Henderson * otherwise; require no other % within the template.
175f6880b7fSAlex Bennée */
1764e51069dSRichard Henderson
1774e51069dSRichard Henderson typedef enum {
1784e51069dSRichard Henderson vft_error,
1794e51069dSRichard Henderson vft_stderr,
1804e51069dSRichard Henderson vft_strdup,
1814e51069dSRichard Henderson vft_pid_printf,
1824e51069dSRichard Henderson } ValidFilenameTemplateResult;
1834e51069dSRichard Henderson
1844e51069dSRichard Henderson static ValidFilenameTemplateResult
valid_filename_template(const char * filename,bool per_thread,Error ** errp)1854e51069dSRichard Henderson valid_filename_template(const char *filename, bool per_thread, Error **errp)
1864e51069dSRichard Henderson {
187e144a605SSalvador Fandino if (filename) {
188e144a605SSalvador Fandino char *pidstr = strstr(filename, "%");
189144539d3SRichard Henderson
190f6880b7fSAlex Bennée if (pidstr) {
191f6880b7fSAlex Bennée /* We only accept one %d, no other format strings */
192f6880b7fSAlex Bennée if (pidstr[1] != 'd' || strchr(pidstr + 2, '%')) {
1934e51069dSRichard Henderson error_setg(errp, "Bad logfile template: %s", filename);
1944e51069dSRichard Henderson return 0;
1954e51069dSRichard Henderson }
1964e51069dSRichard Henderson return per_thread ? vft_strdup : vft_pid_printf;
1974e51069dSRichard Henderson }
1984e51069dSRichard Henderson }
1994e51069dSRichard Henderson if (per_thread) {
2004e51069dSRichard Henderson error_setg(errp, "Filename template with '%%d' required for 'tid'");
2014e51069dSRichard Henderson return vft_error;
2024e51069dSRichard Henderson }
2034e51069dSRichard Henderson return filename ? vft_strdup : vft_stderr;
2044e51069dSRichard Henderson }
2054e51069dSRichard Henderson
2064e51069dSRichard Henderson /* enable or disable low levels log */
qemu_set_log_internal(const char * filename,bool changed_name,int log_flags,Error ** errp)2074e51069dSRichard Henderson static bool qemu_set_log_internal(const char *filename, bool changed_name,
2084e51069dSRichard Henderson int log_flags, Error **errp)
2094e51069dSRichard Henderson {
2104e51069dSRichard Henderson bool need_to_open_file;
2114e51069dSRichard Henderson bool daemonized;
2124e51069dSRichard Henderson bool per_thread;
2134e51069dSRichard Henderson FILE *logfile;
2144e51069dSRichard Henderson
2154e51069dSRichard Henderson QEMU_LOCK_GUARD(&global_mutex);
2164e51069dSRichard Henderson logfile = global_file;
2174e51069dSRichard Henderson
218479b350eSGreg Kurz /* The per-thread flag is immutable. */
219479b350eSGreg Kurz if (log_per_thread) {
220479b350eSGreg Kurz log_flags |= LOG_PER_THREAD;
221524fc737SGreg Kurz } else {
222524fc737SGreg Kurz if (global_filename) {
223524fc737SGreg Kurz log_flags &= ~LOG_PER_THREAD;
224524fc737SGreg Kurz }
225479b350eSGreg Kurz }
226479b350eSGreg Kurz
2274e51069dSRichard Henderson per_thread = log_flags & LOG_PER_THREAD;
2284e51069dSRichard Henderson
2294e51069dSRichard Henderson if (changed_name) {
2304e51069dSRichard Henderson char *newname = NULL;
2314e51069dSRichard Henderson
2324e51069dSRichard Henderson /*
2334e51069dSRichard Henderson * Once threads start opening their own log files, we have no
2344e51069dSRichard Henderson * easy mechanism to tell them all to close and re-open.
2354e51069dSRichard Henderson * There seems little cause to do so either -- this option
2364e51069dSRichard Henderson * will most often be used at user-only startup.
2374e51069dSRichard Henderson */
2384e51069dSRichard Henderson if (log_per_thread) {
2394e51069dSRichard Henderson error_setg(errp, "Cannot change log filename after setting 'tid'");
240e2c7c6a4SRichard Henderson return false;
241f6880b7fSAlex Bennée }
2424e51069dSRichard Henderson
2434e51069dSRichard Henderson switch (valid_filename_template(filename, per_thread, errp)) {
2444e51069dSRichard Henderson case vft_error:
2454e51069dSRichard Henderson return false;
2464e51069dSRichard Henderson case vft_stderr:
2474e51069dSRichard Henderson break;
2484e51069dSRichard Henderson case vft_strdup:
249144539d3SRichard Henderson newname = g_strdup(filename);
2504e51069dSRichard Henderson break;
2514e51069dSRichard Henderson case vft_pid_printf:
2524e51069dSRichard Henderson newname = g_strdup_printf(filename, getpid());
2534e51069dSRichard Henderson break;
254e144a605SSalvador Fandino }
255e144a605SSalvador Fandino
25642266464SRichard Henderson g_free(global_filename);
25742266464SRichard Henderson global_filename = newname;
258144539d3SRichard Henderson filename = newname;
259144539d3SRichard Henderson } else {
26042266464SRichard Henderson filename = global_filename;
2614e51069dSRichard Henderson if (per_thread &&
2624e51069dSRichard Henderson valid_filename_template(filename, true, errp) == vft_error) {
2634e51069dSRichard Henderson return false;
2644e51069dSRichard Henderson }
265144539d3SRichard Henderson }
266144539d3SRichard Henderson
2674e51069dSRichard Henderson /* Once the per-thread flag is set, it cannot be unset. */
2684e51069dSRichard Henderson if (per_thread) {
2694e51069dSRichard Henderson log_per_thread = true;
2704e51069dSRichard Henderson }
2714e51069dSRichard Henderson /* The flag itself is not relevant for need_to_open_file. */
2724e51069dSRichard Henderson log_flags &= ~LOG_PER_THREAD;
273144539d3SRichard Henderson #ifdef CONFIG_TRACE_LOG
274144539d3SRichard Henderson log_flags |= LOG_TRACE;
275144539d3SRichard Henderson #endif
276144539d3SRichard Henderson qemu_loglevel = log_flags;
277144539d3SRichard Henderson
2789b063b7eSGreg Kurz daemonized = is_daemonized();
2799b063b7eSGreg Kurz need_to_open_file = false;
2809b063b7eSGreg Kurz if (!daemonized) {
281144539d3SRichard Henderson /*
2829b063b7eSGreg Kurz * If not daemonized we only log if qemu_loglevel is set, either to
2839b063b7eSGreg Kurz * stderr or to a file (if there is a filename).
2849b063b7eSGreg Kurz * If per-thread, open the file for each thread in qemu_log_trylock().
2859b063b7eSGreg Kurz */
2869b063b7eSGreg Kurz need_to_open_file = qemu_loglevel && !log_per_thread;
2879b063b7eSGreg Kurz } else {
2889b063b7eSGreg Kurz /*
28942266464SRichard Henderson * If we are daemonized, we will only log if there is a filename.
290144539d3SRichard Henderson */
2919b063b7eSGreg Kurz need_to_open_file = filename != NULL;
2929b063b7eSGreg Kurz }
293144539d3SRichard Henderson
29459bde213SPaolo Bonzini if (logfile) {
29559bde213SPaolo Bonzini fflush(logfile);
29659bde213SPaolo Bonzini if (changed_name && logfile != stderr) {
29730f5a73aSRichard Henderson RCUCloseFILE *r = g_new0(RCUCloseFILE, 1);
29830f5a73aSRichard Henderson r->fd = logfile;
29959bde213SPaolo Bonzini qatomic_rcu_set(&global_file, NULL);
30030f5a73aSRichard Henderson call_rcu(r, rcu_close_file, rcu);
301f05142d5SFiona Ebner }
302f05142d5SFiona Ebner if (changed_name) {
30392b24cb7SRichard Henderson logfile = NULL;
304144539d3SRichard Henderson }
30559bde213SPaolo Bonzini }
30692b24cb7SRichard Henderson
3079b063b7eSGreg Kurz if (log_per_thread && daemonized) {
3089b063b7eSGreg Kurz logfile = thread_file;
3099b063b7eSGreg Kurz }
3109b063b7eSGreg Kurz
311144539d3SRichard Henderson if (!logfile && need_to_open_file) {
312144539d3SRichard Henderson if (filename) {
3139b063b7eSGreg Kurz if (log_per_thread) {
3149b063b7eSGreg Kurz logfile = qemu_log_trylock_with_err(errp);
3159b063b7eSGreg Kurz if (!logfile) {
3169b063b7eSGreg Kurz return false;
3179b063b7eSGreg Kurz }
3189b063b7eSGreg Kurz qemu_log_unlock(logfile);
3199b063b7eSGreg Kurz } else {
32059bde213SPaolo Bonzini logfile = fopen(filename, "w");
32130f5a73aSRichard Henderson if (!logfile) {
322144539d3SRichard Henderson error_setg_errno(errp, errno, "Error opening logfile %s",
323144539d3SRichard Henderson filename);
324144539d3SRichard Henderson return false;
325144539d3SRichard Henderson }
3269b063b7eSGreg Kurz }
327144539d3SRichard Henderson /* In case we are a daemon redirect stderr to logfile */
328beab3447SRichard Henderson if (daemonized) {
32930f5a73aSRichard Henderson dup2(fileno(logfile), STDERR_FILENO);
33030f5a73aSRichard Henderson fclose(logfile);
3319b063b7eSGreg Kurz /*
3329b063b7eSGreg Kurz * This will skip closing logfile in rcu_close_file()
3339b063b7eSGreg Kurz * or qemu_log_thread_cleanup().
3349b063b7eSGreg Kurz */
33530f5a73aSRichard Henderson logfile = stderr;
336144539d3SRichard Henderson }
337144539d3SRichard Henderson } else {
338144539d3SRichard Henderson /* Default to stderr if no log file specified */
339beab3447SRichard Henderson assert(!daemonized);
34030f5a73aSRichard Henderson logfile = stderr;
341144539d3SRichard Henderson }
342144539d3SRichard Henderson
3439b063b7eSGreg Kurz if (log_per_thread && daemonized) {
3449b063b7eSGreg Kurz thread_file = logfile;
3459b063b7eSGreg Kurz } else {
3468ae58d60SRichard Henderson qatomic_rcu_set(&global_file, logfile);
347144539d3SRichard Henderson }
3489b063b7eSGreg Kurz }
349144539d3SRichard Henderson return true;
350144539d3SRichard Henderson }
351144539d3SRichard Henderson
qemu_set_log(int log_flags,Error ** errp)352144539d3SRichard Henderson bool qemu_set_log(int log_flags, Error **errp)
353144539d3SRichard Henderson {
354144539d3SRichard Henderson return qemu_set_log_internal(NULL, false, log_flags, errp);
355144539d3SRichard Henderson }
356144539d3SRichard Henderson
qemu_set_log_filename(const char * filename,Error ** errp)357144539d3SRichard Henderson bool qemu_set_log_filename(const char *filename, Error **errp)
358144539d3SRichard Henderson {
359144539d3SRichard Henderson return qemu_set_log_internal(filename, true, qemu_loglevel, errp);
360144539d3SRichard Henderson }
361144539d3SRichard Henderson
qemu_set_log_filename_flags(const char * name,int flags,Error ** errp)362144539d3SRichard Henderson bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp)
363144539d3SRichard Henderson {
364144539d3SRichard Henderson return qemu_set_log_internal(name, true, flags, errp);
365d890d50dSDenis V. Lunev }
366d890d50dSDenis V. Lunev
3673514552eSAlex Bennée /* Returns true if addr is in our debug filter or no filter defined
3683514552eSAlex Bennée */
qemu_log_in_addr_range(uint64_t addr)3693514552eSAlex Bennée bool qemu_log_in_addr_range(uint64_t addr)
3703514552eSAlex Bennée {
3713514552eSAlex Bennée if (debug_regions) {
3723514552eSAlex Bennée int i = 0;
3733514552eSAlex Bennée for (i = 0; i < debug_regions->len; i++) {
37458e19e6eSMarkus Armbruster Range *range = &g_array_index(debug_regions, Range, i);
375a0efbf16SMarkus Armbruster if (range_contains(range, addr)) {
3763514552eSAlex Bennée return true;
3773514552eSAlex Bennée }
3783514552eSAlex Bennée }
3793514552eSAlex Bennée return false;
3803514552eSAlex Bennée } else {
3813514552eSAlex Bennée return true;
3823514552eSAlex Bennée }
3833514552eSAlex Bennée }
3843514552eSAlex Bennée
3853514552eSAlex Bennée
qemu_set_dfilter_ranges(const char * filter_spec,Error ** errp)386bd6fee9fSMarkus Armbruster void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp)
3873514552eSAlex Bennée {
3883514552eSAlex Bennée gchar **ranges = g_strsplit(filter_spec, ",", 0);
389bd6fee9fSMarkus Armbruster int i;
3902ec62faeSMarkus Armbruster
3912ec62faeSMarkus Armbruster if (debug_regions) {
3922ec62faeSMarkus Armbruster g_array_unref(debug_regions);
3932ec62faeSMarkus Armbruster debug_regions = NULL;
3942ec62faeSMarkus Armbruster }
3952ec62faeSMarkus Armbruster
3963514552eSAlex Bennée debug_regions = g_array_sized_new(FALSE, FALSE,
3973514552eSAlex Bennée sizeof(Range), g_strv_length(ranges));
398bd6fee9fSMarkus Armbruster for (i = 0; ranges[i]; i++) {
399bd6fee9fSMarkus Armbruster const char *r = ranges[i];
400bd6fee9fSMarkus Armbruster const char *range_op, *r2, *e;
40158e19e6eSMarkus Armbruster uint64_t r1val, r2val, lob, upb;
402bd6fee9fSMarkus Armbruster struct Range range;
403bd6fee9fSMarkus Armbruster
404bd6fee9fSMarkus Armbruster range_op = strstr(r, "-");
405bd6fee9fSMarkus Armbruster r2 = range_op ? range_op + 1 : NULL;
4063514552eSAlex Bennée if (!range_op) {
4073514552eSAlex Bennée range_op = strstr(r, "+");
4083514552eSAlex Bennée r2 = range_op ? range_op + 1 : NULL;
4093514552eSAlex Bennée }
4103514552eSAlex Bennée if (!range_op) {
4113514552eSAlex Bennée range_op = strstr(r, "..");
4123514552eSAlex Bennée r2 = range_op ? range_op + 2 : NULL;
4133514552eSAlex Bennée }
414bd6fee9fSMarkus Armbruster if (!range_op) {
415bd6fee9fSMarkus Armbruster error_setg(errp, "Bad range specifier");
416bd6fee9fSMarkus Armbruster goto out;
417bd6fee9fSMarkus Armbruster }
4183514552eSAlex Bennée
419b30d1886SMarkus Armbruster if (qemu_strtou64(r, &e, 0, &r1val)
420bd6fee9fSMarkus Armbruster || e != range_op) {
421bd6fee9fSMarkus Armbruster error_setg(errp, "Invalid number to the left of %.*s",
422bd6fee9fSMarkus Armbruster (int)(r2 - range_op), range_op);
423bd6fee9fSMarkus Armbruster goto out;
424bd6fee9fSMarkus Armbruster }
425b30d1886SMarkus Armbruster if (qemu_strtou64(r2, NULL, 0, &r2val)) {
426bd6fee9fSMarkus Armbruster error_setg(errp, "Invalid number to the right of %.*s",
427bd6fee9fSMarkus Armbruster (int)(r2 - range_op), range_op);
428bd6fee9fSMarkus Armbruster goto out;
429bd6fee9fSMarkus Armbruster }
4303514552eSAlex Bennée
4313514552eSAlex Bennée switch (*range_op) {
4323514552eSAlex Bennée case '+':
43358e19e6eSMarkus Armbruster lob = r1val;
43458e19e6eSMarkus Armbruster upb = r1val + r2val - 1;
4353514552eSAlex Bennée break;
4363514552eSAlex Bennée case '-':
43758e19e6eSMarkus Armbruster upb = r1val;
43858e19e6eSMarkus Armbruster lob = r1val - (r2val - 1);
4393514552eSAlex Bennée break;
4403514552eSAlex Bennée case '.':
44158e19e6eSMarkus Armbruster lob = r1val;
44258e19e6eSMarkus Armbruster upb = r2val;
4433514552eSAlex Bennée break;
4443514552eSAlex Bennée default:
4453514552eSAlex Bennée g_assert_not_reached();
4463514552eSAlex Bennée }
44758eeb83cSMarkus Armbruster if (lob > upb) {
44858e19e6eSMarkus Armbruster error_setg(errp, "Invalid range");
44958e19e6eSMarkus Armbruster goto out;
45058e19e6eSMarkus Armbruster }
451a0efbf16SMarkus Armbruster range_set_bounds(&range, lob, upb);
4523514552eSAlex Bennée g_array_append_val(debug_regions, range);
4533514552eSAlex Bennée }
454bd6fee9fSMarkus Armbruster out:
4553514552eSAlex Bennée g_strfreev(ranges);
4563514552eSAlex Bennée }
4573514552eSAlex Bennée
458d890d50dSDenis V. Lunev const QEMULogItem qemu_log_items[] = {
459d890d50dSDenis V. Lunev { CPU_LOG_TB_OUT_ASM, "out_asm",
460d890d50dSDenis V. Lunev "show generated host assembly code for each compiled TB" },
461d890d50dSDenis V. Lunev { CPU_LOG_TB_IN_ASM, "in_asm",
462d890d50dSDenis V. Lunev "show target assembly code for each compiled TB" },
463d890d50dSDenis V. Lunev { CPU_LOG_TB_OP, "op",
464d890d50dSDenis V. Lunev "show micro ops for each compiled TB" },
465d890d50dSDenis V. Lunev { CPU_LOG_TB_OP_OPT, "op_opt",
4665a18407fSRichard Henderson "show micro ops after optimization" },
4675a18407fSRichard Henderson { CPU_LOG_TB_OP_IND, "op_ind",
4685a18407fSRichard Henderson "show micro ops before indirect lowering" },
469b384c734SRichard Henderson #ifdef CONFIG_PLUGIN
470b384c734SRichard Henderson { LOG_TB_OP_PLUGIN, "op_plugin",
471b384c734SRichard Henderson "show micro ops before plugin injection" },
472b384c734SRichard Henderson #endif
473d890d50dSDenis V. Lunev { CPU_LOG_INT, "int",
474d890d50dSDenis V. Lunev "show interrupts/exceptions in short format" },
475d890d50dSDenis V. Lunev { CPU_LOG_EXEC, "exec",
476d890d50dSDenis V. Lunev "show trace before each executed TB (lots of logs)" },
477d890d50dSDenis V. Lunev { CPU_LOG_TB_CPU, "cpu",
47854195736SAlex Bennée "show CPU registers before entering a TB (lots of logs)" },
479ae765180SPeter Maydell { CPU_LOG_TB_FPU, "fpu",
480ae765180SPeter Maydell "include FPU registers in the 'cpu' logging" },
481d890d50dSDenis V. Lunev { CPU_LOG_MMU, "mmu",
482d890d50dSDenis V. Lunev "log MMU-related activities" },
483d890d50dSDenis V. Lunev { CPU_LOG_PCALL, "pcall",
484d890d50dSDenis V. Lunev "x86 only: show protected mode far calls/returns/exceptions" },
485d890d50dSDenis V. Lunev { CPU_LOG_RESET, "cpu_reset",
486d890d50dSDenis V. Lunev "show CPU state before CPU resets" },
487d890d50dSDenis V. Lunev { LOG_UNIMP, "unimp",
488d890d50dSDenis V. Lunev "log unimplemented functionality" },
489d890d50dSDenis V. Lunev { LOG_GUEST_ERROR, "guest_errors",
490d890d50dSDenis V. Lunev "log when the guest OS does something invalid (eg accessing a\n"
491d890d50dSDenis V. Lunev "non-existent register)" },
492d890d50dSDenis V. Lunev { CPU_LOG_PAGE, "page",
493d890d50dSDenis V. Lunev "dump pages at beginning of user mode emulation" },
494d890d50dSDenis V. Lunev { CPU_LOG_TB_NOCHAIN, "nochain",
495d890d50dSDenis V. Lunev "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n"
496d890d50dSDenis V. Lunev "complete traces" },
497ca76a669SAlex Bennée #ifdef CONFIG_PLUGIN
498cb9291e5SBALATON Zoltan { CPU_LOG_PLUGIN, "plugin", "output from TCG plugins"},
499ca76a669SAlex Bennée #endif
5004b25a506SJosh Kunz { LOG_STRACE, "strace",
5014b25a506SJosh Kunz "log every user-mode syscall, its input, and its result" },
5024e51069dSRichard Henderson { LOG_PER_THREAD, "tid",
5034e51069dSRichard Henderson "open a separate log file per thread; filename must contain '%d'" },
504b84694deSIvan Klokov { CPU_LOG_TB_VPU, "vpu",
505b84694deSIvan Klokov "include VPU registers in the 'cpu' logging" },
506d890d50dSDenis V. Lunev { 0, NULL, NULL },
507d890d50dSDenis V. Lunev };
508d890d50dSDenis V. Lunev
509d890d50dSDenis V. Lunev /* takes a comma separated list of log masks. Return 0 if error. */
qemu_str_to_log_mask(const char * str)510d890d50dSDenis V. Lunev int qemu_str_to_log_mask(const char *str)
511d890d50dSDenis V. Lunev {
512d890d50dSDenis V. Lunev const QEMULogItem *item;
51389d0a64fSDaniel P. Berrange int mask = 0;
51489d0a64fSDaniel P. Berrange char **parts = g_strsplit(str, ",", 0);
51589d0a64fSDaniel P. Berrange char **tmp;
516d890d50dSDenis V. Lunev
51789d0a64fSDaniel P. Berrange for (tmp = parts; tmp && *tmp; tmp++) {
51889d0a64fSDaniel P. Berrange if (g_str_equal(*tmp, "all")) {
519d890d50dSDenis V. Lunev for (item = qemu_log_items; item->mask != 0; item++) {
520d890d50dSDenis V. Lunev mask |= item->mask;
521d890d50dSDenis V. Lunev }
522c84ea00dSPaolo Bonzini #ifdef CONFIG_TRACE_LOG
52389d0a64fSDaniel P. Berrange } else if (g_str_has_prefix(*tmp, "trace:") && (*tmp)[6] != '\0') {
52489d0a64fSDaniel P. Berrange trace_enable_events((*tmp) + 6);
525c84ea00dSPaolo Bonzini mask |= LOG_TRACE;
526c84ea00dSPaolo Bonzini #endif
527d890d50dSDenis V. Lunev } else {
528d890d50dSDenis V. Lunev for (item = qemu_log_items; item->mask != 0; item++) {
52989d0a64fSDaniel P. Berrange if (g_str_equal(*tmp, item->name)) {
530d890d50dSDenis V. Lunev goto found;
531d890d50dSDenis V. Lunev }
532d890d50dSDenis V. Lunev }
53389d0a64fSDaniel P. Berrange goto error;
534d890d50dSDenis V. Lunev found:
535d890d50dSDenis V. Lunev mask |= item->mask;
536c84ea00dSPaolo Bonzini }
537d890d50dSDenis V. Lunev }
53889d0a64fSDaniel P. Berrange
53989d0a64fSDaniel P. Berrange g_strfreev(parts);
540d890d50dSDenis V. Lunev return mask;
54189d0a64fSDaniel P. Berrange
54289d0a64fSDaniel P. Berrange error:
54389d0a64fSDaniel P. Berrange g_strfreev(parts);
54489d0a64fSDaniel P. Berrange return 0;
545d890d50dSDenis V. Lunev }
546d890d50dSDenis V. Lunev
qemu_print_log_usage(FILE * f)547d890d50dSDenis V. Lunev void qemu_print_log_usage(FILE *f)
548d890d50dSDenis V. Lunev {
549d890d50dSDenis V. Lunev const QEMULogItem *item;
550d890d50dSDenis V. Lunev fprintf(f, "Log items (comma separated):\n");
551d890d50dSDenis V. Lunev for (item = qemu_log_items; item->mask != 0; item++) {
552c84ea00dSPaolo Bonzini fprintf(f, "%-15s %s\n", item->name, item->help);
553d890d50dSDenis V. Lunev }
554c84ea00dSPaolo Bonzini #ifdef CONFIG_TRACE_LOG
555c84ea00dSPaolo Bonzini fprintf(f, "trace:PATTERN enable trace events\n");
556c84ea00dSPaolo Bonzini fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n");
557c84ea00dSPaolo Bonzini #endif
558d890d50dSDenis V. Lunev }
559