]>
Commit | Line | Data |
---|---|---|
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/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 | static bool init_trace_on_startup; | |
43 | ||
44 | QemuOptsList qemu_trace_opts = { | |
45 | .name = "trace", | |
46 | .implied_opt_name = "enable", | |
47 | .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head), | |
48 | .desc = { | |
49 | { | |
50 | .name = "enable", | |
51 | .type = QEMU_OPT_STRING, | |
52 | }, | |
53 | { | |
54 | .name = "events", | |
55 | .type = QEMU_OPT_STRING, | |
56 | },{ | |
57 | .name = "file", | |
58 | .type = QEMU_OPT_STRING, | |
59 | }, | |
60 | { /* end of list */ } | |
61 | }, | |
62 | }; | |
63 | ||
64 | ||
65 | void trace_event_register_group(TraceEvent **events) | |
66 | { | |
67 | size_t i; | |
68 | for (i = 0; events[i] != NULL; i++) { | |
69 | events[i]->id = next_id++; | |
70 | if (events[i]->vcpu_id == TRACE_VCPU_EVENT_NONE) { | |
71 | continue; | |
72 | } | |
73 | ||
74 | if (likely(next_vcpu_id < CPU_TRACE_DSTATE_MAX_EVENTS)) { | |
75 | events[i]->vcpu_id = next_vcpu_id++; | |
76 | } else { | |
77 | warn_report("too many vcpu trace events; dropping '%s'", | |
78 | events[i]->name); | |
79 | } | |
80 | } | |
81 | event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1); | |
82 | event_groups[nevent_groups].events = events; | |
83 | nevent_groups++; | |
84 | } | |
85 | ||
86 | ||
87 | TraceEvent *trace_event_name(const char *name) | |
88 | { | |
89 | assert(name != NULL); | |
90 | ||
91 | TraceEventIter iter; | |
92 | TraceEvent *ev; | |
93 | trace_event_iter_init(&iter, NULL); | |
94 | while ((ev = trace_event_iter_next(&iter)) != NULL) { | |
95 | if (strcmp(trace_event_get_name(ev), name) == 0) { | |
96 | return ev; | |
97 | } | |
98 | } | |
99 | return NULL; | |
100 | } | |
101 | ||
102 | void trace_event_iter_init(TraceEventIter *iter, const char *pattern) | |
103 | { | |
104 | iter->event = 0; | |
105 | iter->group = 0; | |
106 | iter->pattern = pattern; | |
107 | } | |
108 | ||
109 | TraceEvent *trace_event_iter_next(TraceEventIter *iter) | |
110 | { | |
111 | while (iter->group < nevent_groups && | |
112 | event_groups[iter->group].events[iter->event] != NULL) { | |
113 | TraceEvent *ev = event_groups[iter->group].events[iter->event]; | |
114 | iter->event++; | |
115 | if (event_groups[iter->group].events[iter->event] == NULL) { | |
116 | iter->event = 0; | |
117 | iter->group++; | |
118 | } | |
119 | if (!iter->pattern || | |
120 | g_pattern_match_simple(iter->pattern, trace_event_get_name(ev))) { | |
121 | return ev; | |
122 | } | |
123 | } | |
124 | ||
125 | return NULL; | |
126 | } | |
127 | ||
128 | void trace_list_events(FILE *f) | |
129 | { | |
130 | TraceEventIter iter; | |
131 | TraceEvent *ev; | |
132 | trace_event_iter_init(&iter, NULL); | |
133 | while ((ev = trace_event_iter_next(&iter)) != NULL) { | |
134 | fprintf(f, "%s\n", trace_event_get_name(ev)); | |
135 | } | |
136 | #ifdef CONFIG_TRACE_DTRACE | |
137 | fprintf(f, "This list of names of trace points may be incomplete " | |
138 | "when using the DTrace/SystemTap backends.\n" | |
139 | "Run 'qemu-trace-stap list %s' to print the full list.\n", | |
140 | error_get_progname()); | |
141 | #endif | |
142 | } | |
143 | ||
144 | static void do_trace_enable_events(const char *line_buf) | |
145 | { | |
146 | const bool enable = ('-' != line_buf[0]); | |
147 | const char *line_ptr = enable ? line_buf : line_buf + 1; | |
148 | TraceEventIter iter; | |
149 | TraceEvent *ev; | |
150 | bool is_pattern = trace_event_is_pattern(line_ptr); | |
151 | ||
152 | trace_event_iter_init(&iter, line_ptr); | |
153 | while ((ev = trace_event_iter_next(&iter)) != NULL) { | |
154 | if (!trace_event_get_state_static(ev)) { | |
155 | if (!is_pattern) { | |
156 | warn_report("trace event '%s' is not traceable", | |
157 | line_ptr); | |
158 | return; | |
159 | } | |
160 | continue; | |
161 | } | |
162 | ||
163 | /* start tracing */ | |
164 | trace_event_set_state_dynamic(ev, enable); | |
165 | if (!is_pattern) { | |
166 | return; | |
167 | } | |
168 | } | |
169 | ||
170 | if (!is_pattern) { | |
171 | warn_report("trace event '%s' does not exist", | |
172 | line_ptr); | |
173 | } | |
174 | } | |
175 | ||
176 | void trace_enable_events(const char *line_buf) | |
177 | { | |
178 | if (is_help_option(line_buf)) { | |
179 | trace_list_events(stdout); | |
180 | if (monitor_cur() == NULL) { | |
181 | exit(0); | |
182 | } | |
183 | } else { | |
184 | do_trace_enable_events(line_buf); | |
185 | } | |
186 | } | |
187 | ||
188 | static void trace_init_events(const char *fname) | |
189 | { | |
190 | Location loc; | |
191 | FILE *fp; | |
192 | char line_buf[1024]; | |
193 | size_t line_idx = 0; | |
194 | ||
195 | if (fname == NULL) { | |
196 | return; | |
197 | } | |
198 | ||
199 | loc_push_none(&loc); | |
200 | loc_set_file(fname, 0); | |
201 | fp = fopen(fname, "r"); | |
202 | if (!fp) { | |
203 | error_report("%s", strerror(errno)); | |
204 | exit(1); | |
205 | } | |
206 | while (fgets(line_buf, sizeof(line_buf), fp)) { | |
207 | loc_set_file(fname, ++line_idx); | |
208 | size_t len = strlen(line_buf); | |
209 | if (len > 1) { /* skip empty lines */ | |
210 | line_buf[len - 1] = '\0'; | |
211 | if ('#' == line_buf[0]) { /* skip commented lines */ | |
212 | continue; | |
213 | } | |
214 | trace_enable_events(line_buf); | |
215 | } | |
216 | } | |
217 | if (fclose(fp) != 0) { | |
218 | loc_set_file(fname, 0); | |
219 | error_report("%s", strerror(errno)); | |
220 | exit(1); | |
221 | } | |
222 | loc_pop(&loc); | |
223 | } | |
224 | ||
225 | void trace_init_file(void) | |
226 | { | |
227 | QemuOpts *opts = qemu_find_opts_singleton("trace"); | |
228 | const char *file = qemu_opt_get(opts, "file"); | |
229 | #ifdef CONFIG_TRACE_SIMPLE | |
230 | st_set_trace_file(file); | |
231 | if (init_trace_on_startup) { | |
232 | st_set_trace_file_enabled(true); | |
233 | } | |
234 | #elif defined CONFIG_TRACE_LOG | |
235 | /* | |
236 | * If both the simple and the log backends are enabled, "--trace file" | |
237 | * only applies to the simple backend; use "-D" for the log | |
238 | * backend. However we should only override -D if we actually have | |
239 | * something to override it with. | |
240 | */ | |
241 | if (file) { | |
242 | qemu_set_log_filename(file, &error_fatal); | |
243 | } | |
244 | #else | |
245 | if (file) { | |
246 | fprintf(stderr, "error: --trace file=...: " | |
247 | "option not supported by the selected tracing backends\n"); | |
248 | exit(1); | |
249 | } | |
250 | #endif | |
251 | } | |
252 | ||
253 | void trace_fini_vcpu(CPUState *vcpu) | |
254 | { | |
255 | TraceEventIter iter; | |
256 | TraceEvent *ev; | |
257 | ||
258 | trace_guest_cpu_exit(vcpu); | |
259 | ||
260 | trace_event_iter_init(&iter, NULL); | |
261 | while ((ev = trace_event_iter_next(&iter)) != NULL) { | |
262 | if (trace_event_is_vcpu(ev) && | |
263 | trace_event_get_state_static(ev) && | |
264 | trace_event_get_vcpu_state_dynamic(vcpu, ev)) { | |
265 | /* must disable to affect the global counter */ | |
266 | trace_event_set_vcpu_state_dynamic(vcpu, ev, false); | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
271 | bool trace_init_backends(void) | |
272 | { | |
273 | #ifdef CONFIG_TRACE_SIMPLE | |
274 | if (!st_init()) { | |
275 | fprintf(stderr, "failed to initialize simple tracing backend.\n"); | |
276 | return false; | |
277 | } | |
278 | #endif | |
279 | ||
280 | #ifdef CONFIG_TRACE_FTRACE | |
281 | if (!ftrace_init()) { | |
282 | fprintf(stderr, "failed to initialize ftrace backend.\n"); | |
283 | return false; | |
284 | } | |
285 | #endif | |
286 | ||
287 | #ifdef CONFIG_TRACE_SYSLOG | |
288 | openlog(NULL, LOG_PID, LOG_DAEMON); | |
289 | #endif | |
290 | ||
291 | return true; | |
292 | } | |
293 | ||
294 | void trace_opt_parse(const char *optarg) | |
295 | { | |
296 | QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"), | |
297 | optarg, true); | |
298 | if (!opts) { | |
299 | exit(1); | |
300 | } | |
301 | if (qemu_opt_get(opts, "enable")) { | |
302 | trace_enable_events(qemu_opt_get(opts, "enable")); | |
303 | } | |
304 | trace_init_events(qemu_opt_get(opts, "events")); | |
305 | init_trace_on_startup = true; | |
306 | qemu_opts_del(opts); | |
307 | } | |
308 | ||
309 | uint32_t trace_get_vcpu_event_count(void) | |
310 | { | |
311 | return next_vcpu_id; | |
312 | } |