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