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