1edb47ec4SLluís /* 2edb47ec4SLluís * Simple trace backend 3edb47ec4SLluís * 4edb47ec4SLluís * Copyright IBM, Corp. 2010 5edb47ec4SLluís * 6edb47ec4SLluís * This work is licensed under the terms of the GNU GPL, version 2. See 7edb47ec4SLluís * the COPYING file in the top-level directory. 8edb47ec4SLluís * 9edb47ec4SLluís */ 10edb47ec4SLluís 11edb47ec4SLluís #include <stdlib.h> 12edb47ec4SLluís #include <stdint.h> 13edb47ec4SLluís #include <stdio.h> 14edb47ec4SLluís #include <time.h> 1585aff158SStefan Hajnoczi #ifndef _WIN32 16edb47ec4SLluís #include <signal.h> 17edb47ec4SLluís #include <pthread.h> 1885aff158SStefan Hajnoczi #endif 19edb47ec4SLluís #include "qemu-timer.h" 20edb47ec4SLluís #include "trace.h" 21e4858974SLluís #include "trace/control.h" 22edb47ec4SLluís 23edb47ec4SLluís /** Trace file header event ID */ 24edb47ec4SLluís #define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */ 25edb47ec4SLluís 26edb47ec4SLluís /** Trace file magic number */ 27edb47ec4SLluís #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL 28edb47ec4SLluís 29edb47ec4SLluís /** Trace file version number, bump if format changes */ 30edb47ec4SLluís #define HEADER_VERSION 0 31edb47ec4SLluís 32edb47ec4SLluís /** Records were dropped event ID */ 33edb47ec4SLluís #define DROPPED_EVENT_ID (~(uint64_t)0 - 1) 34edb47ec4SLluís 35edb47ec4SLluís /** Trace record is valid */ 36edb47ec4SLluís #define TRACE_RECORD_VALID ((uint64_t)1 << 63) 37edb47ec4SLluís 38edb47ec4SLluís /** Trace buffer entry */ 39edb47ec4SLluís typedef struct { 40edb47ec4SLluís uint64_t event; 41edb47ec4SLluís uint64_t timestamp_ns; 42edb47ec4SLluís uint64_t x1; 43edb47ec4SLluís uint64_t x2; 44edb47ec4SLluís uint64_t x3; 45edb47ec4SLluís uint64_t x4; 46edb47ec4SLluís uint64_t x5; 47edb47ec4SLluís uint64_t x6; 48edb47ec4SLluís } TraceRecord; 49edb47ec4SLluís 50edb47ec4SLluís enum { 51edb47ec4SLluís TRACE_BUF_LEN = 4096, 52edb47ec4SLluís TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4, 53edb47ec4SLluís }; 54edb47ec4SLluís 55edb47ec4SLluís /* 56edb47ec4SLluís * Trace records are written out by a dedicated thread. The thread waits for 57edb47ec4SLluís * records to become available, writes them out, and then waits again. 58edb47ec4SLluís */ 5985aff158SStefan Hajnoczi static GStaticMutex trace_lock = G_STATIC_MUTEX_INIT; 6085aff158SStefan Hajnoczi static GCond *trace_available_cond; 6185aff158SStefan Hajnoczi static GCond *trace_empty_cond; 62edb47ec4SLluís static bool trace_available; 63edb47ec4SLluís static bool trace_writeout_enabled; 64edb47ec4SLluís 65edb47ec4SLluís static TraceRecord trace_buf[TRACE_BUF_LEN]; 66edb47ec4SLluís static unsigned int trace_idx; 67edb47ec4SLluís static FILE *trace_fp; 68edb47ec4SLluís static char *trace_file_name = NULL; 69edb47ec4SLluís 70edb47ec4SLluís /** 71edb47ec4SLluís * Read a trace record from the trace buffer 72edb47ec4SLluís * 73edb47ec4SLluís * @idx Trace buffer index 74edb47ec4SLluís * @record Trace record to fill 75edb47ec4SLluís * 76edb47ec4SLluís * Returns false if the record is not valid. 77edb47ec4SLluís */ 78edb47ec4SLluís static bool get_trace_record(unsigned int idx, TraceRecord *record) 79edb47ec4SLluís { 80edb47ec4SLluís if (!(trace_buf[idx].event & TRACE_RECORD_VALID)) { 81edb47ec4SLluís return false; 82edb47ec4SLluís } 83edb47ec4SLluís 84edb47ec4SLluís __sync_synchronize(); /* read memory barrier before accessing record */ 85edb47ec4SLluís 86edb47ec4SLluís *record = trace_buf[idx]; 87edb47ec4SLluís record->event &= ~TRACE_RECORD_VALID; 88edb47ec4SLluís return true; 89edb47ec4SLluís } 90edb47ec4SLluís 91edb47ec4SLluís /** 92edb47ec4SLluís * Kick writeout thread 93edb47ec4SLluís * 94edb47ec4SLluís * @wait Whether to wait for writeout thread to complete 95edb47ec4SLluís */ 96edb47ec4SLluís static void flush_trace_file(bool wait) 97edb47ec4SLluís { 9885aff158SStefan Hajnoczi g_static_mutex_lock(&trace_lock); 99edb47ec4SLluís trace_available = true; 10085aff158SStefan Hajnoczi g_cond_signal(trace_available_cond); 101edb47ec4SLluís 102edb47ec4SLluís if (wait) { 10385aff158SStefan Hajnoczi g_cond_wait(trace_empty_cond, g_static_mutex_get_mutex(&trace_lock)); 104edb47ec4SLluís } 105edb47ec4SLluís 10685aff158SStefan Hajnoczi g_static_mutex_unlock(&trace_lock); 107edb47ec4SLluís } 108edb47ec4SLluís 109edb47ec4SLluís static void wait_for_trace_records_available(void) 110edb47ec4SLluís { 11185aff158SStefan Hajnoczi g_static_mutex_lock(&trace_lock); 112edb47ec4SLluís while (!(trace_available && trace_writeout_enabled)) { 11385aff158SStefan Hajnoczi g_cond_signal(trace_empty_cond); 11485aff158SStefan Hajnoczi g_cond_wait(trace_available_cond, 11585aff158SStefan Hajnoczi g_static_mutex_get_mutex(&trace_lock)); 116edb47ec4SLluís } 117edb47ec4SLluís trace_available = false; 11885aff158SStefan Hajnoczi g_static_mutex_unlock(&trace_lock); 119edb47ec4SLluís } 120edb47ec4SLluís 12185aff158SStefan Hajnoczi static gpointer writeout_thread(gpointer opaque) 122edb47ec4SLluís { 123edb47ec4SLluís TraceRecord record; 124edb47ec4SLluís unsigned int writeout_idx = 0; 125edb47ec4SLluís unsigned int num_available, idx; 126edb47ec4SLluís size_t unused __attribute__ ((unused)); 127edb47ec4SLluís 128edb47ec4SLluís for (;;) { 129edb47ec4SLluís wait_for_trace_records_available(); 130edb47ec4SLluís 131edb47ec4SLluís num_available = trace_idx - writeout_idx; 132edb47ec4SLluís if (num_available > TRACE_BUF_LEN) { 133edb47ec4SLluís record = (TraceRecord){ 134edb47ec4SLluís .event = DROPPED_EVENT_ID, 135edb47ec4SLluís .x1 = num_available, 136edb47ec4SLluís }; 137edb47ec4SLluís unused = fwrite(&record, sizeof(record), 1, trace_fp); 138edb47ec4SLluís writeout_idx += num_available; 139edb47ec4SLluís } 140edb47ec4SLluís 141edb47ec4SLluís idx = writeout_idx % TRACE_BUF_LEN; 142edb47ec4SLluís while (get_trace_record(idx, &record)) { 143edb47ec4SLluís trace_buf[idx].event = 0; /* clear valid bit */ 144edb47ec4SLluís unused = fwrite(&record, sizeof(record), 1, trace_fp); 145edb47ec4SLluís idx = ++writeout_idx % TRACE_BUF_LEN; 146edb47ec4SLluís } 147edb47ec4SLluís 148edb47ec4SLluís fflush(trace_fp); 149edb47ec4SLluís } 150edb47ec4SLluís return NULL; 151edb47ec4SLluís } 152edb47ec4SLluís 153edb47ec4SLluís static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, 154edb47ec4SLluís uint64_t x4, uint64_t x5, uint64_t x6) 155edb47ec4SLluís { 156edb47ec4SLluís unsigned int idx; 157edb47ec4SLluís uint64_t timestamp; 158edb47ec4SLluís 159edb47ec4SLluís if (!trace_list[event].state) { 160edb47ec4SLluís return; 161edb47ec4SLluís } 162edb47ec4SLluís 163edb47ec4SLluís timestamp = get_clock(); 164edb47ec4SLluís 16585aff158SStefan Hajnoczi idx = g_atomic_int_exchange_and_add((gint *)&trace_idx, 1) % TRACE_BUF_LEN; 166edb47ec4SLluís trace_buf[idx] = (TraceRecord){ 167edb47ec4SLluís .event = event, 168edb47ec4SLluís .timestamp_ns = timestamp, 169edb47ec4SLluís .x1 = x1, 170edb47ec4SLluís .x2 = x2, 171edb47ec4SLluís .x3 = x3, 172edb47ec4SLluís .x4 = x4, 173edb47ec4SLluís .x5 = x5, 174edb47ec4SLluís .x6 = x6, 175edb47ec4SLluís }; 176edb47ec4SLluís __sync_synchronize(); /* write barrier before marking as valid */ 177edb47ec4SLluís trace_buf[idx].event |= TRACE_RECORD_VALID; 178edb47ec4SLluís 179edb47ec4SLluís if ((idx + 1) % TRACE_BUF_FLUSH_THRESHOLD == 0) { 180edb47ec4SLluís flush_trace_file(false); 181edb47ec4SLluís } 182edb47ec4SLluís } 183edb47ec4SLluís 184edb47ec4SLluís void trace0(TraceEventID event) 185edb47ec4SLluís { 186edb47ec4SLluís trace(event, 0, 0, 0, 0, 0, 0); 187edb47ec4SLluís } 188edb47ec4SLluís 189edb47ec4SLluís void trace1(TraceEventID event, uint64_t x1) 190edb47ec4SLluís { 191edb47ec4SLluís trace(event, x1, 0, 0, 0, 0, 0); 192edb47ec4SLluís } 193edb47ec4SLluís 194edb47ec4SLluís void trace2(TraceEventID event, uint64_t x1, uint64_t x2) 195edb47ec4SLluís { 196edb47ec4SLluís trace(event, x1, x2, 0, 0, 0, 0); 197edb47ec4SLluís } 198edb47ec4SLluís 199edb47ec4SLluís void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3) 200edb47ec4SLluís { 201edb47ec4SLluís trace(event, x1, x2, x3, 0, 0, 0); 202edb47ec4SLluís } 203edb47ec4SLluís 204edb47ec4SLluís void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4) 205edb47ec4SLluís { 206edb47ec4SLluís trace(event, x1, x2, x3, x4, 0, 0); 207edb47ec4SLluís } 208edb47ec4SLluís 209edb47ec4SLluís void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5) 210edb47ec4SLluís { 211edb47ec4SLluís trace(event, x1, x2, x3, x4, x5, 0); 212edb47ec4SLluís } 213edb47ec4SLluís 214edb47ec4SLluís void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) 215edb47ec4SLluís { 216edb47ec4SLluís trace(event, x1, x2, x3, x4, x5, x6); 217edb47ec4SLluís } 218edb47ec4SLluís 219edb47ec4SLluís void st_set_trace_file_enabled(bool enable) 220edb47ec4SLluís { 221edb47ec4SLluís if (enable == !!trace_fp) { 222edb47ec4SLluís return; /* no change */ 223edb47ec4SLluís } 224edb47ec4SLluís 225edb47ec4SLluís /* Halt trace writeout */ 226edb47ec4SLluís flush_trace_file(true); 227edb47ec4SLluís trace_writeout_enabled = false; 228edb47ec4SLluís flush_trace_file(true); 229edb47ec4SLluís 230edb47ec4SLluís if (enable) { 231edb47ec4SLluís static const TraceRecord header = { 232edb47ec4SLluís .event = HEADER_EVENT_ID, 233edb47ec4SLluís .timestamp_ns = HEADER_MAGIC, 234edb47ec4SLluís .x1 = HEADER_VERSION, 235edb47ec4SLluís }; 236edb47ec4SLluís 2376c2a4074SStefan Hajnoczi trace_fp = fopen(trace_file_name, "wb"); 238edb47ec4SLluís if (!trace_fp) { 239edb47ec4SLluís return; 240edb47ec4SLluís } 241edb47ec4SLluís 242edb47ec4SLluís if (fwrite(&header, sizeof header, 1, trace_fp) != 1) { 243edb47ec4SLluís fclose(trace_fp); 244edb47ec4SLluís trace_fp = NULL; 245edb47ec4SLluís return; 246edb47ec4SLluís } 247edb47ec4SLluís 248edb47ec4SLluís /* Resume trace writeout */ 249edb47ec4SLluís trace_writeout_enabled = true; 250edb47ec4SLluís flush_trace_file(false); 251edb47ec4SLluís } else { 252edb47ec4SLluís fclose(trace_fp); 253edb47ec4SLluís trace_fp = NULL; 254edb47ec4SLluís } 255edb47ec4SLluís } 256edb47ec4SLluís 257edb47ec4SLluís /** 258edb47ec4SLluís * Set the name of a trace file 259edb47ec4SLluís * 260edb47ec4SLluís * @file The trace file name or NULL for the default name-<pid> set at 261edb47ec4SLluís * config time 262edb47ec4SLluís */ 263edb47ec4SLluís bool st_set_trace_file(const char *file) 264edb47ec4SLluís { 265edb47ec4SLluís st_set_trace_file_enabled(false); 266edb47ec4SLluís 267edb47ec4SLluís free(trace_file_name); 268edb47ec4SLluís 269edb47ec4SLluís if (!file) { 270edb47ec4SLluís if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) { 271edb47ec4SLluís trace_file_name = NULL; 272edb47ec4SLluís return false; 273edb47ec4SLluís } 274edb47ec4SLluís } else { 275edb47ec4SLluís if (asprintf(&trace_file_name, "%s", file) < 0) { 276edb47ec4SLluís trace_file_name = NULL; 277edb47ec4SLluís return false; 278edb47ec4SLluís } 279edb47ec4SLluís } 280edb47ec4SLluís 281edb47ec4SLluís st_set_trace_file_enabled(true); 282edb47ec4SLluís return true; 283edb47ec4SLluís } 284edb47ec4SLluís 285edb47ec4SLluís void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) 286edb47ec4SLluís { 287edb47ec4SLluís stream_printf(stream, "Trace file \"%s\" %s.\n", 288edb47ec4SLluís trace_file_name, trace_fp ? "on" : "off"); 289edb47ec4SLluís } 290edb47ec4SLluís 291edb47ec4SLluís void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) 292edb47ec4SLluís { 293edb47ec4SLluís unsigned int i; 294edb47ec4SLluís 295edb47ec4SLluís for (i = 0; i < TRACE_BUF_LEN; i++) { 296edb47ec4SLluís TraceRecord record; 297edb47ec4SLluís 298edb47ec4SLluís if (!get_trace_record(i, &record)) { 299edb47ec4SLluís continue; 300edb47ec4SLluís } 301edb47ec4SLluís stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64 302edb47ec4SLluís " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n", 303edb47ec4SLluís record.event, record.x1, record.x2, 304edb47ec4SLluís record.x3, record.x4, record.x5, 305edb47ec4SLluís record.x6); 306edb47ec4SLluís } 307edb47ec4SLluís } 308edb47ec4SLluís 309fc764105SLluís void st_flush_trace_buffer(void) 310fc764105SLluís { 311fc764105SLluís flush_trace_file(true); 312fc764105SLluís } 313fc764105SLluís 314fc764105SLluís void trace_print_events(FILE *stream, fprintf_function stream_printf) 315edb47ec4SLluís { 316edb47ec4SLluís unsigned int i; 317edb47ec4SLluís 318edb47ec4SLluís for (i = 0; i < NR_TRACE_EVENTS; i++) { 319edb47ec4SLluís stream_printf(stream, "%s [Event ID %u] : state %u\n", 320edb47ec4SLluís trace_list[i].tp_name, i, trace_list[i].state); 321edb47ec4SLluís } 322edb47ec4SLluís } 323edb47ec4SLluís 324fc764105SLluís bool trace_event_set_state(const char *name, bool state) 325edb47ec4SLluís { 326edb47ec4SLluís unsigned int i; 327454e202dSMark Wu unsigned int len; 328454e202dSMark Wu bool wildcard = false; 329454e202dSMark Wu bool matched = false; 330edb47ec4SLluís 331454e202dSMark Wu len = strlen(name); 332454e202dSMark Wu if (len > 0 && name[len - 1] == '*') { 333454e202dSMark Wu wildcard = true; 334454e202dSMark Wu len -= 1; 335454e202dSMark Wu } 336edb47ec4SLluís for (i = 0; i < NR_TRACE_EVENTS; i++) { 337454e202dSMark Wu if (wildcard) { 338454e202dSMark Wu if (!strncmp(trace_list[i].tp_name, name, len)) { 339454e202dSMark Wu trace_list[i].state = state; 340454e202dSMark Wu matched = true; 341454e202dSMark Wu } 342454e202dSMark Wu continue; 343454e202dSMark Wu } 344edb47ec4SLluís if (!strcmp(trace_list[i].tp_name, name)) { 345fc764105SLluís trace_list[i].state = state; 346edb47ec4SLluís return true; 347edb47ec4SLluís } 348edb47ec4SLluís } 349454e202dSMark Wu return matched; 350edb47ec4SLluís } 351edb47ec4SLluís 35285aff158SStefan Hajnoczi /* Helper function to create a thread with signals blocked. Use glib's 35385aff158SStefan Hajnoczi * portable threads since QEMU abstractions cannot be used due to reentrancy in 35485aff158SStefan Hajnoczi * the tracer. Also note the signal masking on POSIX hosts so that the thread 35585aff158SStefan Hajnoczi * does not steal signals when the rest of the program wants them blocked. 35685aff158SStefan Hajnoczi */ 35785aff158SStefan Hajnoczi static GThread *trace_thread_create(GThreadFunc fn) 358edb47ec4SLluís { 35985aff158SStefan Hajnoczi GThread *thread; 36085aff158SStefan Hajnoczi #ifndef _WIN32 361edb47ec4SLluís sigset_t set, oldset; 362edb47ec4SLluís 363edb47ec4SLluís sigfillset(&set); 364edb47ec4SLluís pthread_sigmask(SIG_SETMASK, &set, &oldset); 36585aff158SStefan Hajnoczi #endif 366*db3bf869SJun Koi thread = g_thread_create(fn, NULL, FALSE, NULL); 36785aff158SStefan Hajnoczi #ifndef _WIN32 368edb47ec4SLluís pthread_sigmask(SIG_SETMASK, &oldset, NULL); 36985aff158SStefan Hajnoczi #endif 370edb47ec4SLluís 37185aff158SStefan Hajnoczi return thread; 37285aff158SStefan Hajnoczi } 37385aff158SStefan Hajnoczi 37485aff158SStefan Hajnoczi bool trace_backend_init(const char *events, const char *file) 37585aff158SStefan Hajnoczi { 37685aff158SStefan Hajnoczi GThread *thread; 37785aff158SStefan Hajnoczi 37885aff158SStefan Hajnoczi if (!g_thread_supported()) { 37942ed3727SAlon Levy #if !GLIB_CHECK_VERSION(2, 31, 0) 38085aff158SStefan Hajnoczi g_thread_init(NULL); 38142ed3727SAlon Levy #else 38242ed3727SAlon Levy fprintf(stderr, "glib threading failed to initialize.\n"); 38342ed3727SAlon Levy exit(1); 38442ed3727SAlon Levy #endif 38585aff158SStefan Hajnoczi } 38685aff158SStefan Hajnoczi 38785aff158SStefan Hajnoczi trace_available_cond = g_cond_new(); 38885aff158SStefan Hajnoczi trace_empty_cond = g_cond_new(); 38985aff158SStefan Hajnoczi 39085aff158SStefan Hajnoczi thread = trace_thread_create(writeout_thread); 39185aff158SStefan Hajnoczi if (!thread) { 392e4858974SLluís fprintf(stderr, "warning: unable to initialize simple trace backend\n"); 39385aff158SStefan Hajnoczi return false; 39485aff158SStefan Hajnoczi } 39585aff158SStefan Hajnoczi 396edb47ec4SLluís atexit(st_flush_trace_buffer); 39723d15e86SLluís trace_backend_init_events(events); 398edb47ec4SLluís st_set_trace_file(file); 399edb47ec4SLluís return true; 400edb47ec4SLluís } 401