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