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    }
  72    event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1);
  73    event_groups[nevent_groups].events = events;
  74    nevent_groups++;
  75
  76#ifdef CONFIG_TRACE_SIMPLE
  77    st_init_group(nevent_groups - 1);
  78#endif
  79}
  80
  81
  82TraceEvent *trace_event_name(const char *name)
  83{
  84    assert(name != NULL);
  85
  86    TraceEventIter iter;
  87    TraceEvent *ev;
  88    trace_event_iter_init_all(&iter);
  89    while ((ev = trace_event_iter_next(&iter)) != NULL) {
  90        if (strcmp(trace_event_get_name(ev), name) == 0) {
  91            return ev;
  92        }
  93    }
  94    return NULL;
  95}
  96
  97void trace_event_iter_init_all(TraceEventIter *iter)
  98{
  99    iter->event = 0;
 100    iter->group = 0;
 101    iter->group_id = -1;
 102    iter->pattern = NULL;
 103}
 104
 105void trace_event_iter_init_pattern(TraceEventIter *iter, const char *pattern)
 106{
 107    trace_event_iter_init_all(iter);
 108    iter->pattern = pattern;
 109}
 110
 111void trace_event_iter_init_group(TraceEventIter *iter, size_t group_id)
 112{
 113    trace_event_iter_init_all(iter);
 114    iter->group_id = group_id;
 115}
 116
 117TraceEvent *trace_event_iter_next(TraceEventIter *iter)
 118{
 119    while (iter->group < nevent_groups &&
 120           event_groups[iter->group].events[iter->event] != NULL) {
 121        TraceEvent *ev = event_groups[iter->group].events[iter->event];
 122        size_t group = iter->group;
 123        iter->event++;
 124        if (event_groups[iter->group].events[iter->event] == NULL) {
 125            iter->event = 0;
 126            iter->group++;
 127        }
 128        if (iter->pattern &&
 129            !g_pattern_match_simple(iter->pattern, trace_event_get_name(ev))) {
 130            continue;
 131        }
 132        if (iter->group_id != -1 &&
 133            iter->group_id != group) {
 134            continue;
 135        }
 136        return ev;
 137    }
 138
 139    return NULL;
 140}
 141
 142void trace_list_events(FILE *f)
 143{
 144    TraceEventIter iter;
 145    TraceEvent *ev;
 146    trace_event_iter_init_all(&iter);
 147    while ((ev = trace_event_iter_next(&iter)) != NULL) {
 148        fprintf(f, "%s\n", trace_event_get_name(ev));
 149    }
 150#ifdef CONFIG_TRACE_DTRACE
 151    fprintf(f, "This list of names of trace points may be incomplete "
 152               "when using the DTrace/SystemTap backends.\n"
 153               "Run 'qemu-trace-stap list %s' to print the full list.\n",
 154            g_get_prgname());
 155#endif
 156}
 157
 158static void do_trace_enable_events(const char *line_buf)
 159{
 160    const bool enable = ('-' != line_buf[0]);
 161    const char *line_ptr = enable ? line_buf : line_buf + 1;
 162    TraceEventIter iter;
 163    TraceEvent *ev;
 164    bool is_pattern = trace_event_is_pattern(line_ptr);
 165
 166    trace_event_iter_init_pattern(&iter, line_ptr);
 167    while ((ev = trace_event_iter_next(&iter)) != NULL) {
 168        if (!trace_event_get_state_static(ev)) {
 169            if (!is_pattern) {
 170                warn_report("trace event '%s' is not traceable",
 171                            line_ptr);
 172                return;
 173            }
 174            continue;
 175        }
 176
 177        /* start tracing */
 178        trace_event_set_state_dynamic(ev, enable);
 179        if (!is_pattern) {
 180            return;
 181        }
 182    }
 183
 184    if (!is_pattern) {
 185        warn_report("trace event '%s' does not exist",
 186                    line_ptr);
 187    }
 188}
 189
 190void trace_enable_events(const char *line_buf)
 191{
 192    if (is_help_option(line_buf)) {
 193        trace_list_events(stdout);
 194        if (monitor_cur() == NULL) {
 195            exit(0);
 196        }
 197    } else {
 198        do_trace_enable_events(line_buf);
 199    }
 200}
 201
 202static void trace_init_events(const char *fname)
 203{
 204    Location loc;
 205    FILE *fp;
 206    char line_buf[1024];
 207    size_t line_idx = 0;
 208
 209    if (fname == NULL) {
 210        return;
 211    }
 212
 213    loc_push_none(&loc);
 214    loc_set_file(fname, 0);
 215    fp = fopen(fname, "r");
 216    if (!fp) {
 217        error_report("%s", strerror(errno));
 218        exit(1);
 219    }
 220    while (fgets(line_buf, sizeof(line_buf), fp)) {
 221        loc_set_file(fname, ++line_idx);
 222        size_t len = strlen(line_buf);
 223        if (len > 1) {              /* skip empty lines */
 224            line_buf[len - 1] = '\0';
 225            if ('#' == line_buf[0]) { /* skip commented lines */
 226                continue;
 227            }
 228            trace_enable_events(line_buf);
 229        }
 230    }
 231    if (fclose(fp) != 0) {
 232        loc_set_file(fname, 0);
 233        error_report("%s", strerror(errno));
 234        exit(1);
 235    }
 236    loc_pop(&loc);
 237}
 238
 239void trace_init_file(void)
 240{
 241#ifdef CONFIG_TRACE_SIMPLE
 242    st_set_trace_file(trace_opts_file);
 243    if (init_trace_on_startup) {
 244        st_set_trace_file_enabled(true);
 245    }
 246#elif defined CONFIG_TRACE_LOG
 247    /*
 248     * If both the simple and the log backends are enabled, "--trace file"
 249     * only applies to the simple backend; use "-D" for the log
 250     * backend. However we should only override -D if we actually have
 251     * something to override it with.
 252     */
 253    if (trace_opts_file) {
 254        qemu_set_log_filename(trace_opts_file, &error_fatal);
 255    }
 256#else
 257    if (trace_opts_file) {
 258        fprintf(stderr, "error: --trace file=...: "
 259                "option not supported by the selected tracing backends\n");
 260        exit(1);
 261    }
 262#endif
 263}
 264
 265bool trace_init_backends(void)
 266{
 267#ifdef CONFIG_TRACE_SIMPLE
 268    if (!st_init()) {
 269        fprintf(stderr, "failed to initialize simple tracing backend.\n");
 270        return false;
 271    }
 272#endif
 273
 274#ifdef CONFIG_TRACE_FTRACE
 275    if (!ftrace_init()) {
 276        fprintf(stderr, "failed to initialize ftrace backend.\n");
 277        return false;
 278    }
 279#endif
 280
 281#ifdef CONFIG_TRACE_SYSLOG
 282    openlog(NULL, LOG_PID, LOG_DAEMON);
 283#endif
 284
 285    return true;
 286}
 287
 288void trace_opt_parse(const char *optarg)
 289{
 290    QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"),
 291                                             optarg, true);
 292    if (!opts) {
 293        exit(1);
 294    }
 295    if (qemu_opt_get(opts, "enable")) {
 296        trace_enable_events(qemu_opt_get(opts, "enable"));
 297    }
 298    trace_init_events(qemu_opt_get(opts, "events"));
 299    init_trace_on_startup = true;
 300    g_free(trace_opts_file);
 301    trace_opts_file = g_strdup(qemu_opt_get(opts, "file"));
 302    qemu_opts_del(opts);
 303}
 304
 305uint32_t trace_get_vcpu_event_count(void)
 306{
 307    return next_vcpu_id;
 308}
 309