qemu/trace/control.c
<<
>>
Prefs
   1/*
   2 * Interface for configuring and controlling the state of tracing events.
   3 *
   4 * Copyright (C) 2011-2016 LluĂ­s Vilanova <vilanova@ac.upc.edu>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   7 * See the COPYING file in the top-level directory.
   8 */
   9
  10#include "qemu/osdep.h"
  11#include "trace/control.h"
  12#include "qemu/help_option.h"
  13#include "qemu/option.h"
  14#ifdef CONFIG_TRACE_SIMPLE
  15#include "trace/simple.h"
  16#endif
  17#ifdef CONFIG_TRACE_FTRACE
  18#include "trace/ftrace.h"
  19#endif
  20#ifdef CONFIG_TRACE_LOG
  21#include "qemu/log.h"
  22#endif
  23#ifdef CONFIG_TRACE_SYSLOG
  24#include <syslog.h>
  25#endif
  26#include "qapi/error.h"
  27#include "qemu/error-report.h"
  28#include "qemu/config-file.h"
  29#include "monitor/monitor.h"
  30#include "trace/trace-root.h"
  31
  32int trace_events_enabled_count;
  33
  34typedef struct TraceEventGroup {
  35    TraceEvent **events;
  36} TraceEventGroup;
  37
  38static TraceEventGroup *event_groups;
  39static size_t nevent_groups;
  40static uint32_t next_id;
  41static uint32_t next_vcpu_id;
  42static bool init_trace_on_startup;
  43static char *trace_opts_file;
  44
  45QemuOptsList qemu_trace_opts = {
  46    .name = "trace",
  47    .implied_opt_name = "enable",
  48    .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head),
  49    .desc = {
  50        {
  51            .name = "enable",
  52            .type = QEMU_OPT_STRING,
  53        },
  54        {
  55            .name = "events",
  56            .type = QEMU_OPT_STRING,
  57        },{
  58            .name = "file",
  59            .type = QEMU_OPT_STRING,
  60        },
  61        { /* end of list */ }
  62    },
  63};
  64
  65
  66void trace_event_register_group(TraceEvent **events)
  67{
  68    size_t i;
  69    for (i = 0; events[i] != NULL; i++) {
  70        events[i]->id = next_id++;
  71        if (events[i]->vcpu_id == TRACE_VCPU_EVENT_NONE) {
  72            continue;
  73        }
  74
  75        if (likely(next_vcpu_id < CPU_TRACE_DSTATE_MAX_EVENTS)) {
  76            events[i]->vcpu_id = next_vcpu_id++;
  77        } else {
  78            warn_report("too many vcpu trace events; dropping '%s'",
  79                        events[i]->name);
  80        }
  81    }
  82    event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1);
  83    event_groups[nevent_groups].events = events;
  84    nevent_groups++;
  85}
  86
  87
  88TraceEvent *trace_event_name(const char *name)
  89{
  90    assert(name != NULL);
  91
  92    TraceEventIter iter;
  93    TraceEvent *ev;
  94    trace_event_iter_init(&iter, NULL);
  95    while ((ev = trace_event_iter_next(&iter)) != NULL) {
  96        if (strcmp(trace_event_get_name(ev), name) == 0) {
  97            return ev;
  98        }
  99    }
 100    return NULL;
 101}
 102
 103void trace_event_iter_init(TraceEventIter *iter, const char *pattern)
 104{
 105    iter->event = 0;
 106    iter->group = 0;
 107    iter->pattern = pattern;
 108}
 109
 110TraceEvent *trace_event_iter_next(TraceEventIter *iter)
 111{
 112    while (iter->group < nevent_groups &&
 113           event_groups[iter->group].events[iter->event] != NULL) {
 114        TraceEvent *ev = event_groups[iter->group].events[iter->event];
 115        iter->event++;
 116        if (event_groups[iter->group].events[iter->event] == NULL) {
 117            iter->event = 0;
 118            iter->group++;
 119        }
 120        if (!iter->pattern ||
 121            g_pattern_match_simple(iter->pattern, trace_event_get_name(ev))) {
 122            return ev;
 123        }
 124    }
 125
 126    return NULL;
 127}
 128
 129void trace_list_events(FILE *f)
 130{
 131    TraceEventIter iter;
 132    TraceEvent *ev;
 133    trace_event_iter_init(&iter, NULL);
 134    while ((ev = trace_event_iter_next(&iter)) != NULL) {
 135        fprintf(f, "%s\n", trace_event_get_name(ev));
 136    }
 137#ifdef CONFIG_TRACE_DTRACE
 138    fprintf(f, "This list of names of trace points may be incomplete "
 139               "when using the DTrace/SystemTap backends.\n"
 140               "Run 'qemu-trace-stap list %s' to print the full list.\n",
 141            error_get_progname());
 142#endif
 143}
 144
 145static void do_trace_enable_events(const char *line_buf)
 146{
 147    const bool enable = ('-' != line_buf[0]);
 148    const char *line_ptr = enable ? line_buf : line_buf + 1;
 149    TraceEventIter iter;
 150    TraceEvent *ev;
 151    bool is_pattern = trace_event_is_pattern(line_ptr);
 152
 153    trace_event_iter_init(&iter, line_ptr);
 154    while ((ev = trace_event_iter_next(&iter)) != NULL) {
 155        if (!trace_event_get_state_static(ev)) {
 156            if (!is_pattern) {
 157                warn_report("trace event '%s' is not traceable",
 158                            line_ptr);
 159                return;
 160            }
 161            continue;
 162        }
 163
 164        /* start tracing */
 165        trace_event_set_state_dynamic(ev, enable);
 166        if (!is_pattern) {
 167            return;
 168        }
 169    }
 170
 171    if (!is_pattern) {
 172        warn_report("trace event '%s' does not exist",
 173                    line_ptr);
 174    }
 175}
 176
 177void trace_enable_events(const char *line_buf)
 178{
 179    if (is_help_option(line_buf)) {
 180        trace_list_events(stdout);
 181        if (monitor_cur() == NULL) {
 182            exit(0);
 183        }
 184    } else {
 185        do_trace_enable_events(line_buf);
 186    }
 187}
 188
 189static void trace_init_events(const char *fname)
 190{
 191    Location loc;
 192    FILE *fp;
 193    char line_buf[1024];
 194    size_t line_idx = 0;
 195
 196    if (fname == NULL) {
 197        return;
 198    }
 199
 200    loc_push_none(&loc);
 201    loc_set_file(fname, 0);
 202    fp = fopen(fname, "r");
 203    if (!fp) {
 204        error_report("%s", strerror(errno));
 205        exit(1);
 206    }
 207    while (fgets(line_buf, sizeof(line_buf), fp)) {
 208        loc_set_file(fname, ++line_idx);
 209        size_t len = strlen(line_buf);
 210        if (len > 1) {              /* skip empty lines */
 211            line_buf[len - 1] = '\0';
 212            if ('#' == line_buf[0]) { /* skip commented lines */
 213                continue;
 214            }
 215            trace_enable_events(line_buf);
 216        }
 217    }
 218    if (fclose(fp) != 0) {
 219        loc_set_file(fname, 0);
 220        error_report("%s", strerror(errno));
 221        exit(1);
 222    }
 223    loc_pop(&loc);
 224}
 225
 226void trace_init_file(void)
 227{
 228#ifdef CONFIG_TRACE_SIMPLE
 229    st_set_trace_file(trace_opts_file);
 230    if (init_trace_on_startup) {
 231        st_set_trace_file_enabled(true);
 232    }
 233#elif defined CONFIG_TRACE_LOG
 234    /*
 235     * If both the simple and the log backends are enabled, "--trace file"
 236     * only applies to the simple backend; use "-D" for the log
 237     * backend. However we should only override -D if we actually have
 238     * something to override it with.
 239     */
 240    if (trace_opts_file) {
 241        qemu_set_log_filename(trace_opts_file, &error_fatal);
 242    }
 243#else
 244    if (trace_opts_file) {
 245        fprintf(stderr, "error: --trace file=...: "
 246                "option not supported by the selected tracing backends\n");
 247        exit(1);
 248    }
 249#endif
 250}
 251
 252void trace_fini_vcpu(CPUState *vcpu)
 253{
 254    TraceEventIter iter;
 255    TraceEvent *ev;
 256
 257    trace_guest_cpu_exit(vcpu);
 258
 259    trace_event_iter_init(&iter, NULL);
 260    while ((ev = trace_event_iter_next(&iter)) != NULL) {
 261        if (trace_event_is_vcpu(ev) &&
 262            trace_event_get_state_static(ev) &&
 263            trace_event_get_vcpu_state_dynamic(vcpu, ev)) {
 264            /* must disable to affect the global counter */
 265            trace_event_set_vcpu_state_dynamic(vcpu, ev, false);
 266        }
 267    }
 268}
 269
 270bool trace_init_backends(void)
 271{
 272#ifdef CONFIG_TRACE_SIMPLE
 273    if (!st_init()) {
 274        fprintf(stderr, "failed to initialize simple tracing backend.\n");
 275        return false;
 276    }
 277#endif
 278
 279#ifdef CONFIG_TRACE_FTRACE
 280    if (!ftrace_init()) {
 281        fprintf(stderr, "failed to initialize ftrace backend.\n");
 282        return false;
 283    }
 284#endif
 285
 286#ifdef CONFIG_TRACE_SYSLOG
 287    openlog(NULL, LOG_PID, LOG_DAEMON);
 288#endif
 289
 290    return true;
 291}
 292
 293void trace_opt_parse(const char *optarg)
 294{
 295    QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"),
 296                                             optarg, true);
 297    if (!opts) {
 298        exit(1);
 299    }
 300    if (qemu_opt_get(opts, "enable")) {
 301        trace_enable_events(qemu_opt_get(opts, "enable"));
 302    }
 303    trace_init_events(qemu_opt_get(opts, "events"));
 304    init_trace_on_startup = true;
 305    g_free(trace_opts_file);
 306    trace_opts_file = g_strdup(qemu_opt_get(opts, "file"));
 307    qemu_opts_del(opts);
 308}
 309
 310uint32_t trace_get_vcpu_event_count(void)
 311{
 312    return next_vcpu_id;
 313}
 314