xemu/trace/control.c
Daniel P. Berrange b7d48952c3 trace: dynamically allocate trace_dstate in CPUState
The CPUState struct has a bitmap tracking which VCPU
events are currently active. This is indexed based on
the event ID values, and sized according the maximum
TraceEventVCPUID enum value.

When we start dynamically assigning IDs at runtime,
we can't statically declare a bitmap without making
an assumption about the max event count. This problem
can be solved by dynamically allocating the per-CPU
dstate bitmap.

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Lluís Vilanova <vilanova@ac.upc.edu>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-id: 1475588159-30598-15-git-send-email-berrange@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2016-10-12 09:54:52 +02:00

298 lines
6.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"
#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"
int trace_events_enabled_count;
typedef struct TraceEventGroup {
TraceEvent **events;
} TraceEventGroup;
static TraceEventGroup *event_groups;
static size_t nevent_groups;
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)
{
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) {
error_report("WARNING: 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) {
error_report("WARNING: 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
}
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 TRACE_VCPU_EVENT_COUNT;
}