]>
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 <vilanova@ac.upc.edu> |
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" |
243af022 | 30 | #include "trace/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; | |
648b4823 | 42 | static bool init_trace_on_startup; |
9f45a641 | 43 | static char *trace_opts_file; |
fe4db84d | 44 | |
e9e0bb2a DL |
45 | QemuOptsList 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 | ||
fe4db84d DB |
66 | void trace_event_register_group(TraceEvent **events) |
67 | { | |
ca3fa0e8 DB |
68 | size_t i; |
69 | for (i = 0; events[i] != NULL; i++) { | |
70 | events[i]->id = next_id++; | |
ca3fa0e8 | 71 | } |
fe4db84d DB |
72 | event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1); |
73 | event_groups[nevent_groups].events = events; | |
74 | nevent_groups++; | |
263b6e96 GH |
75 | |
76 | #ifdef CONFIG_TRACE_SIMPLE | |
77 | st_init_group(nevent_groups - 1); | |
78 | #endif | |
fe4db84d DB |
79 | } |
80 | ||
81 | ||
b1bae816 LV |
82 | TraceEvent *trace_event_name(const char *name) |
83 | { | |
84 | assert(name != NULL); | |
85 | ||
0d4e995c DB |
86 | TraceEventIter iter; |
87 | TraceEvent *ev; | |
117856c3 | 88 | trace_event_iter_init_all(&iter); |
0d4e995c | 89 | while ((ev = trace_event_iter_next(&iter)) != NULL) { |
b1bae816 LV |
90 | if (strcmp(trace_event_get_name(ev), name) == 0) { |
91 | return ev; | |
92 | } | |
93 | } | |
94 | return NULL; | |
95 | } | |
96 | ||
117856c3 | 97 | void trace_event_iter_init_all(TraceEventIter *iter) |
6a1b0f3a DB |
98 | { |
99 | iter->event = 0; | |
fe4db84d | 100 | iter->group = 0; |
c5cc58b1 | 101 | iter->group_id = -1; |
117856c3 GH |
102 | iter->pattern = NULL; |
103 | } | |
104 | ||
105 | void trace_event_iter_init_pattern(TraceEventIter *iter, const char *pattern) | |
106 | { | |
107 | trace_event_iter_init_all(iter); | |
6a1b0f3a DB |
108 | iter->pattern = pattern; |
109 | } | |
110 | ||
c5cc58b1 GH |
111 | void 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 | ||
6a1b0f3a DB |
117 | TraceEvent *trace_event_iter_next(TraceEventIter *iter) |
118 | { | |
fe4db84d DB |
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]; | |
c5cc58b1 | 122 | size_t group = iter->group; |
6a1b0f3a | 123 | iter->event++; |
fe4db84d DB |
124 | if (event_groups[iter->group].events[iter->event] == NULL) { |
125 | iter->event = 0; | |
126 | iter->group++; | |
127 | } | |
c5cc58b1 GH |
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; | |
6a1b0f3a | 135 | } |
c5cc58b1 | 136 | return ev; |
6a1b0f3a DB |
137 | } |
138 | ||
139 | return NULL; | |
140 | } | |
141 | ||
6745c8a0 | 142 | void trace_list_events(FILE *f) |
e9527dd3 | 143 | { |
0d4e995c DB |
144 | TraceEventIter iter; |
145 | TraceEvent *ev; | |
117856c3 | 146 | trace_event_iter_init_all(&iter); |
0d4e995c | 147 | while ((ev = trace_event_iter_next(&iter)) != NULL) { |
6745c8a0 | 148 | fprintf(f, "%s\n", trace_event_get_name(ev)); |
e9527dd3 | 149 | } |
9f591a5d | 150 | #ifdef CONFIG_TRACE_DTRACE |
6745c8a0 DE |
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", | |
336d354b | 154 | g_get_prgname()); |
9f591a5d | 155 | #endif |
e9527dd3 PB |
156 | } |
157 | ||
158 | static void do_trace_enable_events(const char *line_buf) | |
10578a25 PB |
159 | { |
160 | const bool enable = ('-' != line_buf[0]); | |
161 | const char *line_ptr = enable ? line_buf : line_buf + 1; | |
0d4e995c DB |
162 | TraceEventIter iter; |
163 | TraceEvent *ev; | |
164 | bool is_pattern = trace_event_is_pattern(line_ptr); | |
165 | ||
117856c3 | 166 | trace_event_iter_init_pattern(&iter, line_ptr); |
0d4e995c DB |
167 | while ((ev = trace_event_iter_next(&iter)) != NULL) { |
168 | if (!trace_event_get_state_static(ev)) { | |
169 | if (!is_pattern) { | |
3dc6f869 AF |
170 | warn_report("trace event '%s' is not traceable", |
171 | line_ptr); | |
0d4e995c | 172 | return; |
10578a25 | 173 | } |
0d4e995c | 174 | continue; |
10578a25 | 175 | } |
0d4e995c DB |
176 | |
177 | /* start tracing */ | |
178 | trace_event_set_state_dynamic(ev, enable); | |
179 | if (!is_pattern) { | |
180 | return; | |
10578a25 PB |
181 | } |
182 | } | |
0d4e995c DB |
183 | |
184 | if (!is_pattern) { | |
3dc6f869 AF |
185 | warn_report("trace event '%s' does not exist", |
186 | line_ptr); | |
0d4e995c | 187 | } |
10578a25 PB |
188 | } |
189 | ||
e9527dd3 PB |
190 | void trace_enable_events(const char *line_buf) |
191 | { | |
192 | if (is_help_option(line_buf)) { | |
6745c8a0 | 193 | trace_list_events(stdout); |
947e4744 | 194 | if (monitor_cur() == NULL) { |
acc6809d DL |
195 | exit(0); |
196 | } | |
e9527dd3 PB |
197 | } else { |
198 | do_trace_enable_events(line_buf); | |
199 | } | |
200 | } | |
201 | ||
e9e0bb2a | 202 | static void trace_init_events(const char *fname) |
b1bae816 | 203 | { |
a35d9be6 AK |
204 | Location loc; |
205 | FILE *fp; | |
206 | char line_buf[1024]; | |
207 | size_t line_idx = 0; | |
208 | ||
23d15e86 LV |
209 | if (fname == NULL) { |
210 | return; | |
211 | } | |
212 | ||
a35d9be6 AK |
213 | loc_push_none(&loc); |
214 | loc_set_file(fname, 0); | |
215 | fp = fopen(fname, "r"); | |
23d15e86 | 216 | if (!fp) { |
a35d9be6 | 217 | error_report("%s", strerror(errno)); |
23d15e86 LV |
218 | exit(1); |
219 | } | |
23d15e86 | 220 | while (fgets(line_buf, sizeof(line_buf), fp)) { |
a35d9be6 | 221 | loc_set_file(fname, ++line_idx); |
23d15e86 LV |
222 | size_t len = strlen(line_buf); |
223 | if (len > 1) { /* skip empty lines */ | |
224 | line_buf[len - 1] = '\0'; | |
794b1f96 AK |
225 | if ('#' == line_buf[0]) { /* skip commented lines */ |
226 | continue; | |
227 | } | |
10578a25 | 228 | trace_enable_events(line_buf); |
23d15e86 LV |
229 | } |
230 | } | |
231 | if (fclose(fp) != 0) { | |
a35d9be6 AK |
232 | loc_set_file(fname, 0); |
233 | error_report("%s", strerror(errno)); | |
23d15e86 LV |
234 | exit(1); |
235 | } | |
a35d9be6 | 236 | loc_pop(&loc); |
23d15e86 | 237 | } |
5b808275 | 238 | |
92eecfff | 239 | void trace_init_file(void) |
5b808275 LV |
240 | { |
241 | #ifdef CONFIG_TRACE_SIMPLE | |
9f45a641 | 242 | st_set_trace_file(trace_opts_file); |
648b4823 JD |
243 | if (init_trace_on_startup) { |
244 | st_set_trace_file_enabled(true); | |
245 | } | |
ed7f5f1d | 246 | #elif defined CONFIG_TRACE_LOG |
3d88754e AB |
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. | |
ed7f5f1d | 252 | */ |
9f45a641 PB |
253 | if (trace_opts_file) { |
254 | qemu_set_log_filename(trace_opts_file, &error_fatal); | |
3d88754e | 255 | } |
5b808275 | 256 | #else |
9f45a641 | 257 | if (trace_opts_file) { |
db817b8c | 258 | fprintf(stderr, "error: --trace file=...: " |
5b808275 | 259 | "option not supported by the selected tracing backends\n"); |
41fc57e4 PB |
260 | exit(1); |
261 | } | |
262 | #endif | |
263 | } | |
264 | ||
265 | bool trace_init_backends(void) | |
266 | { | |
267 | #ifdef CONFIG_TRACE_SIMPLE | |
268 | if (!st_init()) { | |
269 | fprintf(stderr, "failed to initialize simple tracing backend.\n"); | |
5b808275 LV |
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 | ||
0a852417 PD |
281 | #ifdef CONFIG_TRACE_SYSLOG |
282 | openlog(NULL, LOG_PID, LOG_DAEMON); | |
283 | #endif | |
284 | ||
5b808275 LV |
285 | return true; |
286 | } | |
e9e0bb2a | 287 | |
8b7b9c5c | 288 | void trace_opt_parse(const char *optstr) |
e9e0bb2a | 289 | { |
e9e0bb2a | 290 | QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"), |
8b7b9c5c | 291 | optstr, true); |
e9e0bb2a DL |
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")); | |
648b4823 | 299 | init_trace_on_startup = true; |
9f45a641 PB |
300 | g_free(trace_opts_file); |
301 | trace_opts_file = g_strdup(qemu_opt_get(opts, "file")); | |
e9e0bb2a | 302 | qemu_opts_del(opts); |
e9e0bb2a | 303 | } |
b7d48952 DB |
304 | |
305 | uint32_t trace_get_vcpu_event_count(void) | |
306 | { | |
ca3fa0e8 | 307 | return next_vcpu_id; |
b7d48952 | 308 | } |