aboutsummaryrefslogtreecommitdiff
path: root/trace/qmp.c
blob: 11d2564e7b5a98c284f2be0b5d147b785ee10ce1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * QMP commands for tracing events.
 *
 * Copyright (C) 2014-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 "qmp-commands.h"
#include "trace/control.h"


static CPUState *get_cpu(bool has_vcpu, int vcpu, Error **errp)
{
    if (has_vcpu) {
        CPUState *cpu = qemu_get_cpu(vcpu);
        if (cpu == NULL) {
            error_setg(errp, "invalid vCPU index %u", vcpu);
        }
        return cpu;
    } else {
        return NULL;
    }
}

static bool check_events(bool has_vcpu, bool ignore_unavailable, bool is_pattern,
                         const char *name, Error **errp)
{
    if (!is_pattern) {
        TraceEvent *ev = trace_event_name(name);

        /* error for non-existing event */
        if (ev == NULL) {
            error_setg(errp, "unknown event \"%s\"", name);
            return false;
        }

        /* error for non-vcpu event */
        if (has_vcpu && !trace_event_is_vcpu(ev)) {
            error_setg(errp, "event \"%s\" is not vCPU-specific", name);
            return false;
        }

        /* error for unavailable event */
        if (!ignore_unavailable && !trace_event_get_state_static(ev)) {
            error_setg(errp, "event \"%s\" is disabled", name);
            return false;
        }

        return true;
    } else {
        /* error for unavailable events */
        TraceEvent *ev = NULL;
        while ((ev = trace_event_pattern(name, ev)) != NULL) {
            if (!ignore_unavailable && !trace_event_get_state_static(ev)) {
                error_setg(errp, "event \"%s\" is disabled", trace_event_get_name(ev));
                return false;
            }
        }
        return true;
    }
}

TraceEventInfoList *qmp_trace_event_get_state(const char *name,
                                              bool has_vcpu, int64_t vcpu,
                                              Error **errp)
{
    Error *err = NULL;
    TraceEventInfoList *events = NULL;
    TraceEvent *ev;
    bool is_pattern = trace_event_is_pattern(name);
    CPUState *cpu;

    /* Check provided vcpu */
    cpu = get_cpu(has_vcpu, vcpu, &err);
    if (err) {
        error_propagate(errp, err);
        return NULL;
    }

    /* Check events */
    if (!check_events(has_vcpu, true, is_pattern, name, errp)) {
        return NULL;
    }

    /* Get states (all errors checked above) */
    ev = NULL;
    while ((ev = trace_event_pattern(name, ev)) != NULL) {
        TraceEventInfoList *elem;
        bool is_vcpu = trace_event_is_vcpu(ev);
        if (has_vcpu && !is_vcpu) {
            continue;
        }

        elem = g_new(TraceEventInfoList, 1);
        elem->value = g_new(TraceEventInfo, 1);
        elem->value->vcpu = is_vcpu;
        elem->value->name = g_strdup(trace_event_get_name(ev));

        if (!trace_event_get_state_static(ev)) {
            elem->value->state = TRACE_EVENT_STATE_UNAVAILABLE;
        } else {
            if (has_vcpu) {
                if (is_vcpu) {
                    if (trace_event_get_vcpu_state_dynamic(cpu, ev)) {
                        elem->value->state = TRACE_EVENT_STATE_ENABLED;
                    } else {
                        elem->value->state = TRACE_EVENT_STATE_DISABLED;
                    }
                }
                /* else: already skipped above */
            } else {
                if (trace_event_get_state_dynamic(ev)) {
                    elem->value->state = TRACE_EVENT_STATE_ENABLED;
                } else {
                    elem->value->state = TRACE_EVENT_STATE_DISABLED;
                }
            }
        }
        elem->next = events;
        events = elem;
    }

    return events;
}

void qmp_trace_event_set_state(const char *name, bool enable,
                               bool has_ignore_unavailable, bool ignore_unavailable,
                               bool has_vcpu, int64_t vcpu,
                               Error **errp)
{
    Error *err = NULL;
    TraceEvent *ev;
    bool is_pattern = trace_event_is_pattern(name);
    CPUState *cpu;

    /* Check provided vcpu */
    cpu = get_cpu(has_vcpu, vcpu, &err);
    if (err) {
        error_propagate(errp, err);
        return;
    }

    /* Check events */
    if (!check_events(has_vcpu, has_ignore_unavailable && ignore_unavailable,
                      is_pattern, name, errp)) {
        return;
    }

    /* Apply changes (all errors checked above) */
    ev = NULL;
    while ((ev = trace_event_pattern(name, ev)) != NULL) {
        if (!trace_event_get_state_static(ev) ||
            (has_vcpu && !trace_event_is_vcpu(ev))) {
            continue;
        }
        if (has_vcpu) {
            trace_event_set_vcpu_state_dynamic(cpu, ev, enable);
        } else {
            trace_event_set_state_dynamic(ev, enable);
        }
    }
}