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