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-root.h" |
31 | |
32 | int trace_events_enabled_count; |
33 | |
34 | typedef struct TraceEventGroup { |
35 | TraceEvent **events; |
36 | } TraceEventGroup; |
37 | |
38 | static TraceEventGroup *event_groups; |
39 | static size_t nevent_groups; |
40 | static uint32_t next_id; |
41 | static uint32_t next_vcpu_id; |
42 | |
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 | |
64 | void trace_event_register_group(TraceEvent **events) |
65 | { |
66 | size_t i; |
67 | for (i = 0; events[i] != NULL; i++) { |
68 | events[i]->id = next_id++; |
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)) { |
74 | events[i]->vcpu_id = next_vcpu_id++; |
75 | } else { |
76 | warn_report("too many vcpu trace events; dropping '%s'" , |
77 | events[i]->name); |
78 | } |
79 | } |
80 | event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1); |
81 | event_groups[nevent_groups].events = events; |
82 | nevent_groups++; |
83 | } |
84 | |
85 | |
86 | TraceEvent *trace_event_name(const char *name) |
87 | { |
88 | assert(name != NULL); |
89 | |
90 | TraceEventIter iter; |
91 | TraceEvent *ev; |
92 | trace_event_iter_init(&iter, NULL); |
93 | while ((ev = trace_event_iter_next(&iter)) != NULL) { |
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 | |
132 | |
133 | void trace_event_iter_init(TraceEventIter *iter, const char *pattern) |
134 | { |
135 | iter->event = 0; |
136 | iter->group = 0; |
137 | iter->pattern = pattern; |
138 | } |
139 | |
140 | TraceEvent *trace_event_iter_next(TraceEventIter *iter) |
141 | { |
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]; |
145 | iter->event++; |
146 | if (event_groups[iter->group].events[iter->event] == NULL) { |
147 | iter->event = 0; |
148 | iter->group++; |
149 | } |
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 | |
160 | void trace_list_events(void) |
161 | { |
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)); |
167 | } |
168 | #ifdef CONFIG_TRACE_DTRACE |
169 | fprintf(stderr, "This list of names of trace points may be incomplete " |
170 | "when using the DTrace/SystemTap backends.\n" |
171 | "Run 'qemu-trace-stap list %s' to print the full list.\n" , |
172 | error_get_progname()); |
173 | #endif |
174 | } |
175 | |
176 | static void do_trace_enable_events(const char *line_buf) |
177 | { |
178 | const bool enable = ('-' != line_buf[0]); |
179 | const char *line_ptr = enable ? line_buf : line_buf + 1; |
180 | TraceEventIter iter; |
181 | TraceEvent *ev; |
182 | bool is_pattern = trace_event_is_pattern(line_ptr); |
183 | |
184 | trace_event_iter_init(&iter, line_ptr); |
185 | while ((ev = trace_event_iter_next(&iter)) != NULL) { |
186 | if (!trace_event_get_state_static(ev)) { |
187 | if (!is_pattern) { |
188 | warn_report("trace event '%s' is not traceable" , |
189 | line_ptr); |
190 | return; |
191 | } |
192 | continue; |
193 | } |
194 | |
195 | /* start tracing */ |
196 | trace_event_set_state_dynamic(ev, enable); |
197 | if (!is_pattern) { |
198 | return; |
199 | } |
200 | } |
201 | |
202 | if (!is_pattern) { |
203 | warn_report("trace event '%s' does not exist" , |
204 | line_ptr); |
205 | } |
206 | } |
207 | |
208 | void trace_enable_events(const char *line_buf) |
209 | { |
210 | if (is_help_option(line_buf)) { |
211 | trace_list_events(); |
212 | if (cur_mon == NULL) { |
213 | exit(0); |
214 | } |
215 | } else { |
216 | do_trace_enable_events(line_buf); |
217 | } |
218 | } |
219 | |
220 | static void trace_init_events(const char *fname) |
221 | { |
222 | Location loc; |
223 | FILE *fp; |
224 | char line_buf[1024]; |
225 | size_t line_idx = 0; |
226 | |
227 | if (fname == NULL) { |
228 | return; |
229 | } |
230 | |
231 | loc_push_none(&loc); |
232 | loc_set_file(fname, 0); |
233 | fp = fopen(fname, "r" ); |
234 | if (!fp) { |
235 | error_report("%s" , strerror(errno)); |
236 | exit(1); |
237 | } |
238 | while (fgets(line_buf, sizeof(line_buf), fp)) { |
239 | loc_set_file(fname, ++line_idx); |
240 | size_t len = strlen(line_buf); |
241 | if (len > 1) { /* skip empty lines */ |
242 | line_buf[len - 1] = '\0'; |
243 | if ('#' == line_buf[0]) { /* skip commented lines */ |
244 | continue; |
245 | } |
246 | trace_enable_events(line_buf); |
247 | } |
248 | } |
249 | if (fclose(fp) != 0) { |
250 | loc_set_file(fname, 0); |
251 | error_report("%s" , strerror(errno)); |
252 | exit(1); |
253 | } |
254 | loc_pop(&loc); |
255 | } |
256 | |
257 | void trace_init_file(const char *file) |
258 | { |
259 | #ifdef CONFIG_TRACE_SIMPLE |
260 | st_set_trace_file(file); |
261 | #elif defined CONFIG_TRACE_LOG |
262 | /* If both the simple and the log backends are enabled, "--trace file" |
263 | * only applies to the simple backend; use "-D" for the log backend. |
264 | */ |
265 | if (file) { |
266 | qemu_set_log_filename(file, &error_fatal); |
267 | } |
268 | #else |
269 | if (file) { |
270 | fprintf(stderr, "error: --trace file=...: " |
271 | "option not supported by the selected tracing backends\n" ); |
272 | exit(1); |
273 | } |
274 | #endif |
275 | } |
276 | |
277 | void trace_fini_vcpu(CPUState *vcpu) |
278 | { |
279 | TraceEventIter iter; |
280 | TraceEvent *ev; |
281 | |
282 | trace_guest_cpu_exit(vcpu); |
283 | |
284 | trace_event_iter_init(&iter, NULL); |
285 | while ((ev = trace_event_iter_next(&iter)) != NULL) { |
286 | if (trace_event_is_vcpu(ev) && |
287 | trace_event_get_state_static(ev) && |
288 | trace_event_get_vcpu_state_dynamic(vcpu, ev)) { |
289 | /* must disable to affect the global counter */ |
290 | trace_event_set_vcpu_state_dynamic(vcpu, ev, false); |
291 | } |
292 | } |
293 | } |
294 | |
295 | bool trace_init_backends(void) |
296 | { |
297 | #ifdef CONFIG_TRACE_SIMPLE |
298 | if (!st_init()) { |
299 | fprintf(stderr, "failed to initialize simple tracing backend.\n" ); |
300 | return false; |
301 | } |
302 | #endif |
303 | |
304 | #ifdef CONFIG_TRACE_FTRACE |
305 | if (!ftrace_init()) { |
306 | fprintf(stderr, "failed to initialize ftrace backend.\n" ); |
307 | return false; |
308 | } |
309 | #endif |
310 | |
311 | #ifdef CONFIG_TRACE_SYSLOG |
312 | openlog(NULL, LOG_PID, LOG_DAEMON); |
313 | #endif |
314 | |
315 | return true; |
316 | } |
317 | |
318 | char *trace_opt_parse(const char *optarg) |
319 | { |
320 | char *trace_file; |
321 | QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace" ), |
322 | optarg, true); |
323 | if (!opts) { |
324 | exit(1); |
325 | } |
326 | if (qemu_opt_get(opts, "enable" )) { |
327 | trace_enable_events(qemu_opt_get(opts, "enable" )); |
328 | } |
329 | trace_init_events(qemu_opt_get(opts, "events" )); |
330 | trace_file = g_strdup(qemu_opt_get(opts, "file" )); |
331 | qemu_opts_del(opts); |
332 | |
333 | return trace_file; |
334 | } |
335 | |
336 | uint32_t trace_get_vcpu_event_count(void) |
337 | { |
338 | return next_vcpu_id; |
339 | } |
340 | |