|
|
1.1 ! root 1: /* ! 2: * Simple trace backend ! 3: * ! 4: * Copyright IBM, Corp. 2010 ! 5: * ! 6: * This work is licensed under the terms of the GNU GPL, version 2. See ! 7: * the COPYING file in the top-level directory. ! 8: * ! 9: */ ! 10: ! 11: #include <stdlib.h> ! 12: #include <stdint.h> ! 13: #include <stdio.h> ! 14: #include <time.h> ! 15: #ifndef _WIN32 ! 16: #include <signal.h> ! 17: #include <pthread.h> ! 18: #endif ! 19: #include "qemu-timer.h" ! 20: #include "trace.h" ! 21: #include "trace/control.h" ! 22: ! 23: /** Trace file header event ID */ ! 24: #define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */ ! 25: ! 26: /** Trace file magic number */ ! 27: #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL ! 28: ! 29: /** Trace file version number, bump if format changes */ ! 30: #define HEADER_VERSION 0 ! 31: ! 32: /** Records were dropped event ID */ ! 33: #define DROPPED_EVENT_ID (~(uint64_t)0 - 1) ! 34: ! 35: /** Trace record is valid */ ! 36: #define TRACE_RECORD_VALID ((uint64_t)1 << 63) ! 37: ! 38: /** Trace buffer entry */ ! 39: typedef struct { ! 40: uint64_t event; ! 41: uint64_t timestamp_ns; ! 42: uint64_t x1; ! 43: uint64_t x2; ! 44: uint64_t x3; ! 45: uint64_t x4; ! 46: uint64_t x5; ! 47: uint64_t x6; ! 48: } TraceRecord; ! 49: ! 50: enum { ! 51: TRACE_BUF_LEN = 4096, ! 52: TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4, ! 53: }; ! 54: ! 55: /* ! 56: * Trace records are written out by a dedicated thread. The thread waits for ! 57: * records to become available, writes them out, and then waits again. ! 58: */ ! 59: static GStaticMutex trace_lock = G_STATIC_MUTEX_INIT; ! 60: static GCond *trace_available_cond; ! 61: static GCond *trace_empty_cond; ! 62: static bool trace_available; ! 63: static bool trace_writeout_enabled; ! 64: ! 65: static TraceRecord trace_buf[TRACE_BUF_LEN]; ! 66: static unsigned int trace_idx; ! 67: static FILE *trace_fp; ! 68: static char *trace_file_name = NULL; ! 69: ! 70: /** ! 71: * Read a trace record from the trace buffer ! 72: * ! 73: * @idx Trace buffer index ! 74: * @record Trace record to fill ! 75: * ! 76: * Returns false if the record is not valid. ! 77: */ ! 78: static bool get_trace_record(unsigned int idx, TraceRecord *record) ! 79: { ! 80: if (!(trace_buf[idx].event & TRACE_RECORD_VALID)) { ! 81: return false; ! 82: } ! 83: ! 84: __sync_synchronize(); /* read memory barrier before accessing record */ ! 85: ! 86: *record = trace_buf[idx]; ! 87: record->event &= ~TRACE_RECORD_VALID; ! 88: return true; ! 89: } ! 90: ! 91: /** ! 92: * Kick writeout thread ! 93: * ! 94: * @wait Whether to wait for writeout thread to complete ! 95: */ ! 96: static void flush_trace_file(bool wait) ! 97: { ! 98: g_static_mutex_lock(&trace_lock); ! 99: trace_available = true; ! 100: g_cond_signal(trace_available_cond); ! 101: ! 102: if (wait) { ! 103: g_cond_wait(trace_empty_cond, g_static_mutex_get_mutex(&trace_lock)); ! 104: } ! 105: ! 106: g_static_mutex_unlock(&trace_lock); ! 107: } ! 108: ! 109: static void wait_for_trace_records_available(void) ! 110: { ! 111: g_static_mutex_lock(&trace_lock); ! 112: while (!(trace_available && trace_writeout_enabled)) { ! 113: g_cond_signal(trace_empty_cond); ! 114: g_cond_wait(trace_available_cond, ! 115: g_static_mutex_get_mutex(&trace_lock)); ! 116: } ! 117: trace_available = false; ! 118: g_static_mutex_unlock(&trace_lock); ! 119: } ! 120: ! 121: static gpointer writeout_thread(gpointer opaque) ! 122: { ! 123: TraceRecord record; ! 124: unsigned int writeout_idx = 0; ! 125: unsigned int num_available, idx; ! 126: size_t unused __attribute__ ((unused)); ! 127: ! 128: for (;;) { ! 129: wait_for_trace_records_available(); ! 130: ! 131: num_available = trace_idx - writeout_idx; ! 132: if (num_available > TRACE_BUF_LEN) { ! 133: record = (TraceRecord){ ! 134: .event = DROPPED_EVENT_ID, ! 135: .x1 = num_available, ! 136: }; ! 137: unused = fwrite(&record, sizeof(record), 1, trace_fp); ! 138: writeout_idx += num_available; ! 139: } ! 140: ! 141: idx = writeout_idx % TRACE_BUF_LEN; ! 142: while (get_trace_record(idx, &record)) { ! 143: trace_buf[idx].event = 0; /* clear valid bit */ ! 144: unused = fwrite(&record, sizeof(record), 1, trace_fp); ! 145: idx = ++writeout_idx % TRACE_BUF_LEN; ! 146: } ! 147: ! 148: fflush(trace_fp); ! 149: } ! 150: return NULL; ! 151: } ! 152: ! 153: static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, ! 154: uint64_t x4, uint64_t x5, uint64_t x6) ! 155: { ! 156: unsigned int idx; ! 157: uint64_t timestamp; ! 158: ! 159: if (!trace_list[event].state) { ! 160: return; ! 161: } ! 162: ! 163: timestamp = get_clock(); ! 164: ! 165: idx = g_atomic_int_exchange_and_add((gint *)&trace_idx, 1) % TRACE_BUF_LEN; ! 166: trace_buf[idx] = (TraceRecord){ ! 167: .event = event, ! 168: .timestamp_ns = timestamp, ! 169: .x1 = x1, ! 170: .x2 = x2, ! 171: .x3 = x3, ! 172: .x4 = x4, ! 173: .x5 = x5, ! 174: .x6 = x6, ! 175: }; ! 176: __sync_synchronize(); /* write barrier before marking as valid */ ! 177: trace_buf[idx].event |= TRACE_RECORD_VALID; ! 178: ! 179: if ((idx + 1) % TRACE_BUF_FLUSH_THRESHOLD == 0) { ! 180: flush_trace_file(false); ! 181: } ! 182: } ! 183: ! 184: void trace0(TraceEventID event) ! 185: { ! 186: trace(event, 0, 0, 0, 0, 0, 0); ! 187: } ! 188: ! 189: void trace1(TraceEventID event, uint64_t x1) ! 190: { ! 191: trace(event, x1, 0, 0, 0, 0, 0); ! 192: } ! 193: ! 194: void trace2(TraceEventID event, uint64_t x1, uint64_t x2) ! 195: { ! 196: trace(event, x1, x2, 0, 0, 0, 0); ! 197: } ! 198: ! 199: void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3) ! 200: { ! 201: trace(event, x1, x2, x3, 0, 0, 0); ! 202: } ! 203: ! 204: void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4) ! 205: { ! 206: trace(event, x1, x2, x3, x4, 0, 0); ! 207: } ! 208: ! 209: void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5) ! 210: { ! 211: trace(event, x1, x2, x3, x4, x5, 0); ! 212: } ! 213: ! 214: void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) ! 215: { ! 216: trace(event, x1, x2, x3, x4, x5, x6); ! 217: } ! 218: ! 219: void st_set_trace_file_enabled(bool enable) ! 220: { ! 221: if (enable == !!trace_fp) { ! 222: return; /* no change */ ! 223: } ! 224: ! 225: /* Halt trace writeout */ ! 226: flush_trace_file(true); ! 227: trace_writeout_enabled = false; ! 228: flush_trace_file(true); ! 229: ! 230: if (enable) { ! 231: static const TraceRecord header = { ! 232: .event = HEADER_EVENT_ID, ! 233: .timestamp_ns = HEADER_MAGIC, ! 234: .x1 = HEADER_VERSION, ! 235: }; ! 236: ! 237: trace_fp = fopen(trace_file_name, "wb"); ! 238: if (!trace_fp) { ! 239: return; ! 240: } ! 241: ! 242: if (fwrite(&header, sizeof header, 1, trace_fp) != 1) { ! 243: fclose(trace_fp); ! 244: trace_fp = NULL; ! 245: return; ! 246: } ! 247: ! 248: /* Resume trace writeout */ ! 249: trace_writeout_enabled = true; ! 250: flush_trace_file(false); ! 251: } else { ! 252: fclose(trace_fp); ! 253: trace_fp = NULL; ! 254: } ! 255: } ! 256: ! 257: /** ! 258: * Set the name of a trace file ! 259: * ! 260: * @file The trace file name or NULL for the default name-<pid> set at ! 261: * config time ! 262: */ ! 263: bool st_set_trace_file(const char *file) ! 264: { ! 265: st_set_trace_file_enabled(false); ! 266: ! 267: free(trace_file_name); ! 268: ! 269: if (!file) { ! 270: if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) { ! 271: trace_file_name = NULL; ! 272: return false; ! 273: } ! 274: } else { ! 275: if (asprintf(&trace_file_name, "%s", file) < 0) { ! 276: trace_file_name = NULL; ! 277: return false; ! 278: } ! 279: } ! 280: ! 281: st_set_trace_file_enabled(true); ! 282: return true; ! 283: } ! 284: ! 285: void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) ! 286: { ! 287: stream_printf(stream, "Trace file \"%s\" %s.\n", ! 288: trace_file_name, trace_fp ? "on" : "off"); ! 289: } ! 290: ! 291: void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) ! 292: { ! 293: unsigned int i; ! 294: ! 295: for (i = 0; i < TRACE_BUF_LEN; i++) { ! 296: TraceRecord record; ! 297: ! 298: if (!get_trace_record(i, &record)) { ! 299: continue; ! 300: } ! 301: stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64 ! 302: " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n", ! 303: record.event, record.x1, record.x2, ! 304: record.x3, record.x4, record.x5, ! 305: record.x6); ! 306: } ! 307: } ! 308: ! 309: void st_flush_trace_buffer(void) ! 310: { ! 311: flush_trace_file(true); ! 312: } ! 313: ! 314: void trace_print_events(FILE *stream, fprintf_function stream_printf) ! 315: { ! 316: unsigned int i; ! 317: ! 318: for (i = 0; i < NR_TRACE_EVENTS; i++) { ! 319: stream_printf(stream, "%s [Event ID %u] : state %u\n", ! 320: trace_list[i].tp_name, i, trace_list[i].state); ! 321: } ! 322: } ! 323: ! 324: bool trace_event_set_state(const char *name, bool state) ! 325: { ! 326: unsigned int i; ! 327: unsigned int len; ! 328: bool wildcard = false; ! 329: bool matched = false; ! 330: ! 331: len = strlen(name); ! 332: if (len > 0 && name[len - 1] == '*') { ! 333: wildcard = true; ! 334: len -= 1; ! 335: } ! 336: for (i = 0; i < NR_TRACE_EVENTS; i++) { ! 337: if (wildcard) { ! 338: if (!strncmp(trace_list[i].tp_name, name, len)) { ! 339: trace_list[i].state = state; ! 340: matched = true; ! 341: } ! 342: continue; ! 343: } ! 344: if (!strcmp(trace_list[i].tp_name, name)) { ! 345: trace_list[i].state = state; ! 346: return true; ! 347: } ! 348: } ! 349: return matched; ! 350: } ! 351: ! 352: /* Helper function to create a thread with signals blocked. Use glib's ! 353: * portable threads since QEMU abstractions cannot be used due to reentrancy in ! 354: * the tracer. Also note the signal masking on POSIX hosts so that the thread ! 355: * does not steal signals when the rest of the program wants them blocked. ! 356: */ ! 357: static GThread *trace_thread_create(GThreadFunc fn) ! 358: { ! 359: GThread *thread; ! 360: #ifndef _WIN32 ! 361: sigset_t set, oldset; ! 362: ! 363: sigfillset(&set); ! 364: pthread_sigmask(SIG_SETMASK, &set, &oldset); ! 365: #endif ! 366: thread = g_thread_create(writeout_thread, NULL, FALSE, NULL); ! 367: #ifndef _WIN32 ! 368: pthread_sigmask(SIG_SETMASK, &oldset, NULL); ! 369: #endif ! 370: ! 371: return thread; ! 372: } ! 373: ! 374: bool trace_backend_init(const char *events, const char *file) ! 375: { ! 376: GThread *thread; ! 377: ! 378: if (!g_thread_supported()) { ! 379: g_thread_init(NULL); ! 380: } ! 381: ! 382: trace_available_cond = g_cond_new(); ! 383: trace_empty_cond = g_cond_new(); ! 384: ! 385: thread = trace_thread_create(writeout_thread); ! 386: if (!thread) { ! 387: fprintf(stderr, "warning: unable to initialize simple trace backend\n"); ! 388: return false; ! 389: } ! 390: ! 391: atexit(st_flush_trace_buffer); ! 392: trace_backend_init_events(events); ! 393: st_set_trace_file(file); ! 394: return true; ! 395: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.