922a01a013
qemu-common.h includes qemu/option.h, but most places that include the
former don't actually need the latter. Drop the include, and add it
to the places that actually need it.
While there, drop superfluous includes of both headers, and
separate #include from file comment with a blank line.
This cleanup makes the number of objects depending on qemu/option.h
drop from 4545 (out of 4743) to 284 in my "build everything" tree.
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180201111846.21846-20-armbru@redhat.com>
[Semantic conflict with commit bdd6a90a9e
in block/nvme.c resolved]
334 lines
7.9 KiB
C
334 lines
7.9 KiB
C
/*
|
|
* Interface for configuring and controlling the state of tracing events.
|
|
*
|
|
* Copyright (C) 2011-2016 Lluís Vilanova <vilanova@ac.upc.edu>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "trace/control.h"
|
|
#include "qemu/help_option.h"
|
|
#include "qemu/option.h"
|
|
#ifdef CONFIG_TRACE_SIMPLE
|
|
#include "trace/simple.h"
|
|
#endif
|
|
#ifdef CONFIG_TRACE_FTRACE
|
|
#include "trace/ftrace.h"
|
|
#endif
|
|
#ifdef CONFIG_TRACE_LOG
|
|
#include "qemu/log.h"
|
|
#endif
|
|
#ifdef CONFIG_TRACE_SYSLOG
|
|
#include <syslog.h>
|
|
#endif
|
|
#include "qapi/error.h"
|
|
#include "qemu/error-report.h"
|
|
#include "qemu/config-file.h"
|
|
#include "monitor/monitor.h"
|
|
#include "trace-root.h"
|
|
|
|
int trace_events_enabled_count;
|
|
|
|
typedef struct TraceEventGroup {
|
|
TraceEvent **events;
|
|
} TraceEventGroup;
|
|
|
|
static TraceEventGroup *event_groups;
|
|
static size_t nevent_groups;
|
|
static uint32_t next_id;
|
|
static uint32_t next_vcpu_id;
|
|
|
|
QemuOptsList qemu_trace_opts = {
|
|
.name = "trace",
|
|
.implied_opt_name = "enable",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = "enable",
|
|
.type = QEMU_OPT_STRING,
|
|
},
|
|
{
|
|
.name = "events",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "file",
|
|
.type = QEMU_OPT_STRING,
|
|
},
|
|
{ /* end of list */ }
|
|
},
|
|
};
|
|
|
|
|
|
void trace_event_register_group(TraceEvent **events)
|
|
{
|
|
size_t i;
|
|
for (i = 0; events[i] != NULL; i++) {
|
|
events[i]->id = next_id++;
|
|
if (events[i]->vcpu_id == TRACE_VCPU_EVENT_NONE) {
|
|
continue;
|
|
}
|
|
|
|
if (likely(next_vcpu_id < CPU_TRACE_DSTATE_MAX_EVENTS)) {
|
|
events[i]->vcpu_id = next_vcpu_id++;
|
|
} else {
|
|
warn_report("too many vcpu trace events; dropping '%s'",
|
|
events[i]->name);
|
|
}
|
|
}
|
|
event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1);
|
|
event_groups[nevent_groups].events = events;
|
|
nevent_groups++;
|
|
}
|
|
|
|
|
|
TraceEvent *trace_event_name(const char *name)
|
|
{
|
|
assert(name != NULL);
|
|
|
|
TraceEventIter iter;
|
|
TraceEvent *ev;
|
|
trace_event_iter_init(&iter, NULL);
|
|
while ((ev = trace_event_iter_next(&iter)) != NULL) {
|
|
if (strcmp(trace_event_get_name(ev), name) == 0) {
|
|
return ev;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static bool pattern_glob(const char *pat, const char *ev)
|
|
{
|
|
while (*pat != '\0' && *ev != '\0') {
|
|
if (*pat == *ev) {
|
|
pat++;
|
|
ev++;
|
|
}
|
|
else if (*pat == '*') {
|
|
if (pattern_glob(pat, ev+1)) {
|
|
return true;
|
|
} else if (pattern_glob(pat+1, ev)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
while (*pat == '*') {
|
|
pat++;
|
|
}
|
|
|
|
if (*pat == '\0' && *ev == '\0') {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void trace_event_iter_init(TraceEventIter *iter, const char *pattern)
|
|
{
|
|
iter->event = 0;
|
|
iter->group = 0;
|
|
iter->pattern = pattern;
|
|
}
|
|
|
|
TraceEvent *trace_event_iter_next(TraceEventIter *iter)
|
|
{
|
|
while (iter->group < nevent_groups &&
|
|
event_groups[iter->group].events[iter->event] != NULL) {
|
|
TraceEvent *ev = event_groups[iter->group].events[iter->event];
|
|
iter->event++;
|
|
if (event_groups[iter->group].events[iter->event] == NULL) {
|
|
iter->event = 0;
|
|
iter->group++;
|
|
}
|
|
if (!iter->pattern ||
|
|
pattern_glob(iter->pattern,
|
|
trace_event_get_name(ev))) {
|
|
return ev;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void trace_list_events(void)
|
|
{
|
|
TraceEventIter iter;
|
|
TraceEvent *ev;
|
|
trace_event_iter_init(&iter, NULL);
|
|
while ((ev = trace_event_iter_next(&iter)) != NULL) {
|
|
fprintf(stderr, "%s\n", trace_event_get_name(ev));
|
|
}
|
|
}
|
|
|
|
static void do_trace_enable_events(const char *line_buf)
|
|
{
|
|
const bool enable = ('-' != line_buf[0]);
|
|
const char *line_ptr = enable ? line_buf : line_buf + 1;
|
|
TraceEventIter iter;
|
|
TraceEvent *ev;
|
|
bool is_pattern = trace_event_is_pattern(line_ptr);
|
|
|
|
trace_event_iter_init(&iter, line_ptr);
|
|
while ((ev = trace_event_iter_next(&iter)) != NULL) {
|
|
if (!trace_event_get_state_static(ev)) {
|
|
if (!is_pattern) {
|
|
warn_report("trace event '%s' is not traceable",
|
|
line_ptr);
|
|
return;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* start tracing */
|
|
trace_event_set_state_dynamic(ev, enable);
|
|
if (!is_pattern) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!is_pattern) {
|
|
warn_report("trace event '%s' does not exist",
|
|
line_ptr);
|
|
}
|
|
}
|
|
|
|
void trace_enable_events(const char *line_buf)
|
|
{
|
|
if (is_help_option(line_buf)) {
|
|
trace_list_events();
|
|
if (cur_mon == NULL) {
|
|
exit(0);
|
|
}
|
|
} else {
|
|
do_trace_enable_events(line_buf);
|
|
}
|
|
}
|
|
|
|
static void trace_init_events(const char *fname)
|
|
{
|
|
Location loc;
|
|
FILE *fp;
|
|
char line_buf[1024];
|
|
size_t line_idx = 0;
|
|
|
|
if (fname == NULL) {
|
|
return;
|
|
}
|
|
|
|
loc_push_none(&loc);
|
|
loc_set_file(fname, 0);
|
|
fp = fopen(fname, "r");
|
|
if (!fp) {
|
|
error_report("%s", strerror(errno));
|
|
exit(1);
|
|
}
|
|
while (fgets(line_buf, sizeof(line_buf), fp)) {
|
|
loc_set_file(fname, ++line_idx);
|
|
size_t len = strlen(line_buf);
|
|
if (len > 1) { /* skip empty lines */
|
|
line_buf[len - 1] = '\0';
|
|
if ('#' == line_buf[0]) { /* skip commented lines */
|
|
continue;
|
|
}
|
|
trace_enable_events(line_buf);
|
|
}
|
|
}
|
|
if (fclose(fp) != 0) {
|
|
loc_set_file(fname, 0);
|
|
error_report("%s", strerror(errno));
|
|
exit(1);
|
|
}
|
|
loc_pop(&loc);
|
|
}
|
|
|
|
void trace_init_file(const char *file)
|
|
{
|
|
#ifdef CONFIG_TRACE_SIMPLE
|
|
st_set_trace_file(file);
|
|
#elif defined CONFIG_TRACE_LOG
|
|
/* If both the simple and the log backends are enabled, "-trace file"
|
|
* only applies to the simple backend; use "-D" for the log backend.
|
|
*/
|
|
if (file) {
|
|
qemu_set_log_filename(file, &error_fatal);
|
|
}
|
|
#else
|
|
if (file) {
|
|
fprintf(stderr, "error: -trace file=...: "
|
|
"option not supported by the selected tracing backends\n");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void trace_fini_vcpu(CPUState *vcpu)
|
|
{
|
|
TraceEventIter iter;
|
|
TraceEvent *ev;
|
|
|
|
trace_guest_cpu_exit(vcpu);
|
|
|
|
trace_event_iter_init(&iter, NULL);
|
|
while ((ev = trace_event_iter_next(&iter)) != NULL) {
|
|
if (trace_event_is_vcpu(ev) &&
|
|
trace_event_get_state_static(ev) &&
|
|
trace_event_get_vcpu_state_dynamic(vcpu, ev)) {
|
|
/* must disable to affect the global counter */
|
|
trace_event_set_vcpu_state_dynamic(vcpu, ev, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool trace_init_backends(void)
|
|
{
|
|
#ifdef CONFIG_TRACE_SIMPLE
|
|
if (!st_init()) {
|
|
fprintf(stderr, "failed to initialize simple tracing backend.\n");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_TRACE_FTRACE
|
|
if (!ftrace_init()) {
|
|
fprintf(stderr, "failed to initialize ftrace backend.\n");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_TRACE_SYSLOG
|
|
openlog(NULL, LOG_PID, LOG_DAEMON);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
char *trace_opt_parse(const char *optarg)
|
|
{
|
|
char *trace_file;
|
|
QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"),
|
|
optarg, true);
|
|
if (!opts) {
|
|
exit(1);
|
|
}
|
|
if (qemu_opt_get(opts, "enable")) {
|
|
trace_enable_events(qemu_opt_get(opts, "enable"));
|
|
}
|
|
trace_init_events(qemu_opt_get(opts, "events"));
|
|
trace_file = g_strdup(qemu_opt_get(opts, "file"));
|
|
qemu_opts_del(opts);
|
|
|
|
return trace_file;
|
|
}
|
|
|
|
uint32_t trace_get_vcpu_event_count(void)
|
|
{
|
|
return next_vcpu_id;
|
|
}
|