]>
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" |
5b808275 LV |
13 | #ifdef CONFIG_TRACE_SIMPLE |
14 | #include "trace/simple.h" | |
15 | #endif | |
16 | #ifdef CONFIG_TRACE_FTRACE | |
17 | #include "trace/ftrace.h" | |
18 | #endif | |
ed7f5f1d PB |
19 | #ifdef CONFIG_TRACE_LOG |
20 | #include "qemu/log.h" | |
21 | #endif | |
0a852417 PD |
22 | #ifdef CONFIG_TRACE_SYSLOG |
23 | #include <syslog.h> | |
24 | #endif | |
daa76aa4 | 25 | #include "qapi/error.h" |
a35d9be6 | 26 | #include "qemu/error-report.h" |
e9e0bb2a | 27 | #include "qemu/config-file.h" |
acc6809d | 28 | #include "monitor/monitor.h" |
23d15e86 | 29 | |
43b48cfc | 30 | int trace_events_enabled_count; |
48151859 LV |
31 | /* |
32 | * Interpretation depends on wether the event has the 'vcpu' property: | |
33 | * - false: Boolean value indicating whether the event is active. | |
34 | * - true : Integral counting the number of vCPUs that have this event enabled. | |
35 | */ | |
36 | uint16_t trace_events_dstate[TRACE_EVENT_COUNT]; | |
43b48cfc | 37 | |
e9e0bb2a DL |
38 | QemuOptsList qemu_trace_opts = { |
39 | .name = "trace", | |
40 | .implied_opt_name = "enable", | |
41 | .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head), | |
42 | .desc = { | |
43 | { | |
44 | .name = "enable", | |
45 | .type = QEMU_OPT_STRING, | |
46 | }, | |
47 | { | |
48 | .name = "events", | |
49 | .type = QEMU_OPT_STRING, | |
50 | },{ | |
51 | .name = "file", | |
52 | .type = QEMU_OPT_STRING, | |
53 | }, | |
54 | { /* end of list */ } | |
55 | }, | |
56 | }; | |
57 | ||
58 | ||
b1bae816 LV |
59 | TraceEvent *trace_event_name(const char *name) |
60 | { | |
61 | assert(name != NULL); | |
62 | ||
63 | TraceEventID i; | |
64 | for (i = 0; i < trace_event_count(); i++) { | |
65 | TraceEvent *ev = trace_event_id(i); | |
66 | if (strcmp(trace_event_get_name(ev), name) == 0) { | |
67 | return ev; | |
68 | } | |
69 | } | |
70 | return NULL; | |
71 | } | |
72 | ||
73 | static bool pattern_glob(const char *pat, const char *ev) | |
74 | { | |
75 | while (*pat != '\0' && *ev != '\0') { | |
76 | if (*pat == *ev) { | |
77 | pat++; | |
78 | ev++; | |
79 | } | |
80 | else if (*pat == '*') { | |
81 | if (pattern_glob(pat, ev+1)) { | |
82 | return true; | |
83 | } else if (pattern_glob(pat+1, ev)) { | |
84 | return true; | |
85 | } else { | |
86 | return false; | |
87 | } | |
88 | } else { | |
89 | return false; | |
90 | } | |
91 | } | |
92 | ||
93 | while (*pat == '*') { | |
94 | pat++; | |
95 | } | |
96 | ||
97 | if (*pat == '\0' && *ev == '\0') { | |
98 | return true; | |
99 | } else { | |
100 | return false; | |
101 | } | |
102 | } | |
103 | ||
104 | TraceEvent *trace_event_pattern(const char *pat, TraceEvent *ev) | |
23d15e86 | 105 | { |
b1bae816 | 106 | assert(pat != NULL); |
ddde8acc | 107 | |
b1bae816 LV |
108 | TraceEventID i; |
109 | ||
110 | if (ev == NULL) { | |
111 | i = -1; | |
112 | } else { | |
113 | i = trace_event_get_id(ev); | |
114 | } | |
115 | i++; | |
116 | ||
117 | while (i < trace_event_count()) { | |
118 | TraceEvent *res = trace_event_id(i); | |
119 | if (pattern_glob(pat, trace_event_get_name(res))) { | |
120 | return res; | |
121 | } | |
122 | i++; | |
123 | } | |
124 | ||
125 | return NULL; | |
126 | } | |
127 | ||
6a1b0f3a DB |
128 | void trace_event_iter_init(TraceEventIter *iter, const char *pattern) |
129 | { | |
130 | iter->event = 0; | |
131 | iter->pattern = pattern; | |
132 | } | |
133 | ||
134 | TraceEvent *trace_event_iter_next(TraceEventIter *iter) | |
135 | { | |
136 | while (iter->event < TRACE_EVENT_COUNT) { | |
137 | TraceEvent *ev = &(trace_events[iter->event]); | |
138 | iter->event++; | |
139 | if (!iter->pattern || | |
140 | pattern_glob(iter->pattern, | |
141 | trace_event_get_name(ev))) { | |
142 | return ev; | |
143 | } | |
144 | } | |
145 | ||
146 | return NULL; | |
147 | } | |
148 | ||
e9527dd3 PB |
149 | void trace_list_events(void) |
150 | { | |
151 | int i; | |
152 | for (i = 0; i < trace_event_count(); i++) { | |
153 | TraceEvent *res = trace_event_id(i); | |
154 | fprintf(stderr, "%s\n", trace_event_get_name(res)); | |
155 | } | |
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; | |
162 | ||
163 | if (trace_event_is_pattern(line_ptr)) { | |
164 | TraceEvent *ev = NULL; | |
165 | while ((ev = trace_event_pattern(line_ptr, ev)) != NULL) { | |
166 | if (trace_event_get_state_static(ev)) { | |
a4d50b1d | 167 | trace_event_set_state_dynamic_init(ev, enable); |
10578a25 PB |
168 | } |
169 | } | |
170 | } else { | |
171 | TraceEvent *ev = trace_event_name(line_ptr); | |
172 | if (ev == NULL) { | |
173 | error_report("WARNING: trace event '%s' does not exist", | |
174 | line_ptr); | |
175 | } else if (!trace_event_get_state_static(ev)) { | |
176 | error_report("WARNING: trace event '%s' is not traceable", | |
177 | line_ptr); | |
178 | } else { | |
a4d50b1d | 179 | trace_event_set_state_dynamic_init(ev, enable); |
10578a25 PB |
180 | } |
181 | } | |
182 | } | |
183 | ||
e9527dd3 PB |
184 | void trace_enable_events(const char *line_buf) |
185 | { | |
186 | if (is_help_option(line_buf)) { | |
187 | trace_list_events(); | |
acc6809d DL |
188 | if (cur_mon == NULL) { |
189 | exit(0); | |
190 | } | |
e9527dd3 PB |
191 | } else { |
192 | do_trace_enable_events(line_buf); | |
193 | } | |
194 | } | |
195 | ||
e9e0bb2a | 196 | static void trace_init_events(const char *fname) |
b1bae816 | 197 | { |
a35d9be6 AK |
198 | Location loc; |
199 | FILE *fp; | |
200 | char line_buf[1024]; | |
201 | size_t line_idx = 0; | |
202 | ||
23d15e86 LV |
203 | if (fname == NULL) { |
204 | return; | |
205 | } | |
206 | ||
a35d9be6 AK |
207 | loc_push_none(&loc); |
208 | loc_set_file(fname, 0); | |
209 | fp = fopen(fname, "r"); | |
23d15e86 | 210 | if (!fp) { |
a35d9be6 | 211 | error_report("%s", strerror(errno)); |
23d15e86 LV |
212 | exit(1); |
213 | } | |
23d15e86 | 214 | while (fgets(line_buf, sizeof(line_buf), fp)) { |
a35d9be6 | 215 | loc_set_file(fname, ++line_idx); |
23d15e86 LV |
216 | size_t len = strlen(line_buf); |
217 | if (len > 1) { /* skip empty lines */ | |
218 | line_buf[len - 1] = '\0'; | |
794b1f96 AK |
219 | if ('#' == line_buf[0]) { /* skip commented lines */ |
220 | continue; | |
221 | } | |
10578a25 | 222 | trace_enable_events(line_buf); |
23d15e86 LV |
223 | } |
224 | } | |
225 | if (fclose(fp) != 0) { | |
a35d9be6 AK |
226 | loc_set_file(fname, 0); |
227 | error_report("%s", strerror(errno)); | |
23d15e86 LV |
228 | exit(1); |
229 | } | |
a35d9be6 | 230 | loc_pop(&loc); |
23d15e86 | 231 | } |
5b808275 | 232 | |
41fc57e4 | 233 | void trace_init_file(const char *file) |
5b808275 LV |
234 | { |
235 | #ifdef CONFIG_TRACE_SIMPLE | |
41fc57e4 | 236 | st_set_trace_file(file); |
ed7f5f1d PB |
237 | #elif defined CONFIG_TRACE_LOG |
238 | /* If both the simple and the log backends are enabled, "-trace file" | |
239 | * only applies to the simple backend; use "-D" for the log backend. | |
240 | */ | |
241 | if (file) { | |
daa76aa4 | 242 | qemu_set_log_filename(file, &error_fatal); |
ed7f5f1d | 243 | } |
5b808275 LV |
244 | #else |
245 | if (file) { | |
246 | fprintf(stderr, "error: -trace file=...: " | |
247 | "option not supported by the selected tracing backends\n"); | |
41fc57e4 PB |
248 | exit(1); |
249 | } | |
250 | #endif | |
251 | } | |
252 | ||
253 | bool trace_init_backends(void) | |
254 | { | |
255 | #ifdef CONFIG_TRACE_SIMPLE | |
256 | if (!st_init()) { | |
257 | fprintf(stderr, "failed to initialize simple tracing backend.\n"); | |
5b808275 LV |
258 | return false; |
259 | } | |
260 | #endif | |
261 | ||
262 | #ifdef CONFIG_TRACE_FTRACE | |
263 | if (!ftrace_init()) { | |
264 | fprintf(stderr, "failed to initialize ftrace backend.\n"); | |
265 | return false; | |
266 | } | |
267 | #endif | |
268 | ||
0a852417 PD |
269 | #ifdef CONFIG_TRACE_SYSLOG |
270 | openlog(NULL, LOG_PID, LOG_DAEMON); | |
271 | #endif | |
272 | ||
5b808275 LV |
273 | return true; |
274 | } | |
e9e0bb2a DL |
275 | |
276 | char *trace_opt_parse(const char *optarg) | |
277 | { | |
278 | char *trace_file; | |
279 | QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"), | |
280 | optarg, true); | |
281 | if (!opts) { | |
282 | exit(1); | |
283 | } | |
284 | if (qemu_opt_get(opts, "enable")) { | |
285 | trace_enable_events(qemu_opt_get(opts, "enable")); | |
286 | } | |
287 | trace_init_events(qemu_opt_get(opts, "events")); | |
288 | trace_file = g_strdup(qemu_opt_get(opts, "file")); | |
289 | qemu_opts_del(opts); | |
290 | ||
291 | return trace_file; | |
292 | } |