xref: /openbmc/qemu/util/log.c (revision f05142d5)
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);
301*f05142d5SFiona Ebner         }
302*f05142d5SFiona 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" },
469d890d50dSDenis V. Lunev     { CPU_LOG_INT, "int",
470d890d50dSDenis V. Lunev       "show interrupts/exceptions in short format" },
471d890d50dSDenis V. Lunev     { CPU_LOG_EXEC, "exec",
472d890d50dSDenis V. Lunev       "show trace before each executed TB (lots of logs)" },
473d890d50dSDenis V. Lunev     { CPU_LOG_TB_CPU, "cpu",
47454195736SAlex Bennée       "show CPU registers before entering a TB (lots of logs)" },
475ae765180SPeter Maydell     { CPU_LOG_TB_FPU, "fpu",
476ae765180SPeter Maydell       "include FPU registers in the 'cpu' logging" },
477d890d50dSDenis V. Lunev     { CPU_LOG_MMU, "mmu",
478d890d50dSDenis V. Lunev       "log MMU-related activities" },
479d890d50dSDenis V. Lunev     { CPU_LOG_PCALL, "pcall",
480d890d50dSDenis V. Lunev       "x86 only: show protected mode far calls/returns/exceptions" },
481d890d50dSDenis V. Lunev     { CPU_LOG_RESET, "cpu_reset",
482d890d50dSDenis V. Lunev       "show CPU state before CPU resets" },
483d890d50dSDenis V. Lunev     { LOG_UNIMP, "unimp",
484d890d50dSDenis V. Lunev       "log unimplemented functionality" },
485d890d50dSDenis V. Lunev     { LOG_GUEST_ERROR, "guest_errors",
486d890d50dSDenis V. Lunev       "log when the guest OS does something invalid (eg accessing a\n"
487d890d50dSDenis V. Lunev       "non-existent register)" },
488d890d50dSDenis V. Lunev     { CPU_LOG_PAGE, "page",
489d890d50dSDenis V. Lunev       "dump pages at beginning of user mode emulation" },
490d890d50dSDenis V. Lunev     { CPU_LOG_TB_NOCHAIN, "nochain",
491d890d50dSDenis V. Lunev       "do not chain compiled TBs so that \"exec\" and \"cpu\" show\n"
492d890d50dSDenis V. Lunev       "complete traces" },
493ca76a669SAlex Bennée #ifdef CONFIG_PLUGIN
494cb9291e5SBALATON Zoltan     { CPU_LOG_PLUGIN, "plugin", "output from TCG plugins"},
495ca76a669SAlex Bennée #endif
4964b25a506SJosh Kunz     { LOG_STRACE, "strace",
4974b25a506SJosh Kunz       "log every user-mode syscall, its input, and its result" },
4984e51069dSRichard Henderson     { LOG_PER_THREAD, "tid",
4994e51069dSRichard Henderson       "open a separate log file per thread; filename must contain '%d'" },
500b84694deSIvan Klokov     { CPU_LOG_TB_VPU, "vpu",
501b84694deSIvan Klokov       "include VPU registers in the 'cpu' logging" },
502d890d50dSDenis V. Lunev     { 0, NULL, NULL },
503d890d50dSDenis V. Lunev };
504d890d50dSDenis V. Lunev 
505d890d50dSDenis V. Lunev /* takes a comma separated list of log masks. Return 0 if error. */
qemu_str_to_log_mask(const char * str)506d890d50dSDenis V. Lunev int qemu_str_to_log_mask(const char *str)
507d890d50dSDenis V. Lunev {
508d890d50dSDenis V. Lunev     const QEMULogItem *item;
50989d0a64fSDaniel P. Berrange     int mask = 0;
51089d0a64fSDaniel P. Berrange     char **parts = g_strsplit(str, ",", 0);
51189d0a64fSDaniel P. Berrange     char **tmp;
512d890d50dSDenis V. Lunev 
51389d0a64fSDaniel P. Berrange     for (tmp = parts; tmp && *tmp; tmp++) {
51489d0a64fSDaniel P. Berrange         if (g_str_equal(*tmp, "all")) {
515d890d50dSDenis V. Lunev             for (item = qemu_log_items; item->mask != 0; item++) {
516d890d50dSDenis V. Lunev                 mask |= item->mask;
517d890d50dSDenis V. Lunev             }
518c84ea00dSPaolo Bonzini #ifdef CONFIG_TRACE_LOG
51989d0a64fSDaniel P. Berrange         } else if (g_str_has_prefix(*tmp, "trace:") && (*tmp)[6] != '\0') {
52089d0a64fSDaniel P. Berrange             trace_enable_events((*tmp) + 6);
521c84ea00dSPaolo Bonzini             mask |= LOG_TRACE;
522c84ea00dSPaolo Bonzini #endif
523d890d50dSDenis V. Lunev         } else {
524d890d50dSDenis V. Lunev             for (item = qemu_log_items; item->mask != 0; item++) {
52589d0a64fSDaniel P. Berrange                 if (g_str_equal(*tmp, item->name)) {
526d890d50dSDenis V. Lunev                     goto found;
527d890d50dSDenis V. Lunev                 }
528d890d50dSDenis V. Lunev             }
52989d0a64fSDaniel P. Berrange             goto error;
530d890d50dSDenis V. Lunev         found:
531d890d50dSDenis V. Lunev             mask |= item->mask;
532c84ea00dSPaolo Bonzini         }
533d890d50dSDenis V. Lunev     }
53489d0a64fSDaniel P. Berrange 
53589d0a64fSDaniel P. Berrange     g_strfreev(parts);
536d890d50dSDenis V. Lunev     return mask;
53789d0a64fSDaniel P. Berrange 
53889d0a64fSDaniel P. Berrange  error:
53989d0a64fSDaniel P. Berrange     g_strfreev(parts);
54089d0a64fSDaniel P. Berrange     return 0;
541d890d50dSDenis V. Lunev }
542d890d50dSDenis V. Lunev 
qemu_print_log_usage(FILE * f)543d890d50dSDenis V. Lunev void qemu_print_log_usage(FILE *f)
544d890d50dSDenis V. Lunev {
545d890d50dSDenis V. Lunev     const QEMULogItem *item;
546d890d50dSDenis V. Lunev     fprintf(f, "Log items (comma separated):\n");
547d890d50dSDenis V. Lunev     for (item = qemu_log_items; item->mask != 0; item++) {
548c84ea00dSPaolo Bonzini         fprintf(f, "%-15s %s\n", item->name, item->help);
549d890d50dSDenis V. Lunev     }
550c84ea00dSPaolo Bonzini #ifdef CONFIG_TRACE_LOG
551c84ea00dSPaolo Bonzini     fprintf(f, "trace:PATTERN   enable trace events\n");
552c84ea00dSPaolo Bonzini     fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n");
553c84ea00dSPaolo Bonzini #endif
554d890d50dSDenis V. Lunev }
555