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