control.c 5.04 KB
Newer Older
1 2 3
/*
 * Interface for configuring and controlling the state of tracing events.
 *
4
 * Copyright (C) 2011-2014 Lluís Vilanova <vilanova@ac.upc.edu>
5
 *
6 7
 * This work is licensed under the terms of the GNU GPL, version 2 or later.
 * See the COPYING file in the top-level directory.
8 9
 */

Peter Maydell's avatar
Peter Maydell committed
10
#include "qemu/osdep.h"
11
#include "trace/control.h"
12
#include "qemu/help_option.h"
13 14 15 16 17 18
#ifdef CONFIG_TRACE_SIMPLE
#include "trace/simple.h"
#endif
#ifdef CONFIG_TRACE_FTRACE
#include "trace/ftrace.h"
#endif
19 20 21
#ifdef CONFIG_TRACE_LOG
#include "qemu/log.h"
#endif
22
#include "qemu/error-report.h"
23
#include "monitor/monitor.h"
24

25
int trace_events_enabled_count;
26
bool trace_events_dstate[TRACE_EVENT_COUNT];
27

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
TraceEvent *trace_event_name(const char *name)
{
    assert(name != NULL);

    TraceEventID i;
    for (i = 0; i < trace_event_count(); i++) {
        TraceEvent *ev = trace_event_id(i);
        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;
    }
}

TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev)
74
{
75
    assert(pat != NULL);
76

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    TraceEventID i;

    if (ev == NULL) {
        i = -1;
    } else {
        i = trace_event_get_id(ev);
    }
    i++;

    while (i < trace_event_count()) {
        TraceEvent *res = trace_event_id(i);
        if (pattern_glob(pat, trace_event_get_name(res))) {
            return res;
        }
        i++;
    }

    return NULL;
}

Paolo Bonzini's avatar
Paolo Bonzini committed
97 98 99 100 101 102 103 104 105 106
void trace_list_events(void)
{
    int i;
    for (i = 0; i < trace_event_count(); i++) {
        TraceEvent *res = trace_event_id(i);
        fprintf(stderr, "%s\n", trace_event_get_name(res));
    }
}

static void do_trace_enable_events(const char *line_buf)
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
{
    const bool enable = ('-' != line_buf[0]);
    const char *line_ptr = enable ? line_buf : line_buf + 1;

    if (trace_event_is_pattern(line_ptr)) {
        TraceEvent *ev = NULL;
        while ((ev = trace_event_pattern(line_ptr, ev)) != NULL) {
            if (trace_event_get_state_static(ev)) {
                trace_event_set_state_dynamic(ev, enable);
            }
        }
    } else {
        TraceEvent *ev = trace_event_name(line_ptr);
        if (ev == NULL) {
            error_report("WARNING: trace event '%s' does not exist",
                         line_ptr);
        } else if (!trace_event_get_state_static(ev)) {
            error_report("WARNING: trace event '%s' is not traceable",
                         line_ptr);
        } else {
            trace_event_set_state_dynamic(ev, enable);
        }
    }
}

Paolo Bonzini's avatar
Paolo Bonzini committed
132 133 134 135
void trace_enable_events(const char *line_buf)
{
    if (is_help_option(line_buf)) {
        trace_list_events();
136 137 138
        if (cur_mon == NULL) {
            exit(0);
        }
Paolo Bonzini's avatar
Paolo Bonzini committed
139 140 141 142 143
    } else {
        do_trace_enable_events(line_buf);
    }
}

144
void trace_init_events(const char *fname)
145
{
146 147 148 149 150
    Location loc;
    FILE *fp;
    char line_buf[1024];
    size_t line_idx = 0;

151 152 153 154
    if (fname == NULL) {
        return;
    }

155 156 157
    loc_push_none(&loc);
    loc_set_file(fname, 0);
    fp = fopen(fname, "r");
158
    if (!fp) {
159
        error_report("%s", strerror(errno));
160 161 162
        exit(1);
    }
    while (fgets(line_buf, sizeof(line_buf), fp)) {
163
        loc_set_file(fname, ++line_idx);
164 165 166
        size_t len = strlen(line_buf);
        if (len > 1) {              /* skip empty lines */
            line_buf[len - 1] = '\0';
167 168 169
            if ('#' == line_buf[0]) { /* skip commented lines */
                continue;
            }
170
            trace_enable_events(line_buf);
171 172 173
        }
    }
    if (fclose(fp) != 0) {
174 175
        loc_set_file(fname, 0);
        error_report("%s", strerror(errno));
176 177
        exit(1);
    }
178
    loc_pop(&loc);
179
}
180

181
void trace_init_file(const char *file)
182 183
{
#ifdef CONFIG_TRACE_SIMPLE
184
    st_set_trace_file(file);
185 186 187 188 189 190 191
#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);
    }
192 193 194 195
#else
    if (file) {
        fprintf(stderr, "error: -trace file=...: "
                "option not supported by the selected tracing backends\n");
196 197 198 199 200 201 202 203 204 205
        exit(1);
    }
#endif
}

bool trace_init_backends(void)
{
#ifdef CONFIG_TRACE_SIMPLE
    if (!st_init()) {
        fprintf(stderr, "failed to initialize simple tracing backend.\n");
206 207 208 209 210 211 212 213 214 215 216 217 218
        return false;
    }
#endif

#ifdef CONFIG_TRACE_FTRACE
    if (!ftrace_init()) {
        fprintf(stderr, "failed to initialize ftrace backend.\n");
        return false;
    }
#endif

    return true;
}