diff options
Diffstat (limited to 'qemu/trace/simple.c')
-rw-r--r-- | qemu/trace/simple.c | 386 |
1 files changed, 0 insertions, 386 deletions
diff --git a/qemu/trace/simple.c b/qemu/trace/simple.c deleted file mode 100644 index 3fdcc8226..000000000 --- a/qemu/trace/simple.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Simple trace backend - * - * Copyright IBM, Corp. 2010 - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#ifndef _WIN32 -#include <pthread.h> -#endif -#include "qemu/timer.h" -#include "trace.h" -#include "trace/control.h" -#include "trace/simple.h" - -/** Trace file header event ID */ -#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */ - -/** Trace file magic number */ -#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL - -/** Trace file version number, bump if format changes */ -#define HEADER_VERSION 3 - -/** Records were dropped event ID */ -#define DROPPED_EVENT_ID (~(uint64_t)0 - 1) - -/** Trace record is valid */ -#define TRACE_RECORD_VALID ((uint64_t)1 << 63) - -/* - * Trace records are written out by a dedicated thread. The thread waits for - * records to become available, writes them out, and then waits again. - */ -static CompatGMutex trace_lock; -static CompatGCond trace_available_cond; -static CompatGCond trace_empty_cond; - -static bool trace_available; -static bool trace_writeout_enabled; - -enum { - TRACE_BUF_LEN = 4096 * 64, - TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4, -}; - -uint8_t trace_buf[TRACE_BUF_LEN]; -static volatile gint trace_idx; -static unsigned int writeout_idx; -static volatile gint dropped_events; -static uint32_t trace_pid; -static FILE *trace_fp; -static char *trace_file_name; - -/* * Trace buffer entry */ -typedef struct { - uint64_t event; /* TraceEventID */ - uint64_t timestamp_ns; - uint32_t length; /* in bytes */ - uint32_t pid; - uint64_t arguments[]; -} TraceRecord; - -typedef struct { - uint64_t header_event_id; /* HEADER_EVENT_ID */ - uint64_t header_magic; /* HEADER_MAGIC */ - uint64_t header_version; /* HEADER_VERSION */ -} TraceLogHeader; - - -static void read_from_buffer(unsigned int idx, void *dataptr, size_t size); -static unsigned int write_to_buffer(unsigned int idx, void *dataptr, size_t size); - -static void clear_buffer_range(unsigned int idx, size_t len) -{ - uint32_t num = 0; - while (num < len) { - if (idx >= TRACE_BUF_LEN) { - idx = idx % TRACE_BUF_LEN; - } - trace_buf[idx++] = 0; - num++; - } -} -/** - * Read a trace record from the trace buffer - * - * @idx Trace buffer index - * @record Trace record to fill - * - * Returns false if the record is not valid. - */ -static bool get_trace_record(unsigned int idx, TraceRecord **recordptr) -{ - uint64_t event_flag = 0; - TraceRecord record; - /* read the event flag to see if its a valid record */ - read_from_buffer(idx, &record, sizeof(event_flag)); - - if (!(record.event & TRACE_RECORD_VALID)) { - return false; - } - - smp_rmb(); /* read memory barrier before accessing record */ - /* read the record header to know record length */ - read_from_buffer(idx, &record, sizeof(TraceRecord)); - *recordptr = malloc(record.length); /* dont use g_malloc, can deadlock when traced */ - /* make a copy of record to avoid being overwritten */ - read_from_buffer(idx, *recordptr, record.length); - smp_rmb(); /* memory barrier before clearing valid flag */ - (*recordptr)->event &= ~TRACE_RECORD_VALID; - /* clear the trace buffer range for consumed record otherwise any byte - * with its MSB set may be considered as a valid event id when the writer - * thread crosses this range of buffer again. - */ - clear_buffer_range(idx, record.length); - return true; -} - -/** - * Kick writeout thread - * - * @wait Whether to wait for writeout thread to complete - */ -static void flush_trace_file(bool wait) -{ - g_mutex_lock(&trace_lock); - trace_available = true; - g_cond_signal(&trace_available_cond); - - if (wait) { - g_cond_wait(&trace_empty_cond, &trace_lock); - } - - g_mutex_unlock(&trace_lock); -} - -static void wait_for_trace_records_available(void) -{ - g_mutex_lock(&trace_lock); - while (!(trace_available && trace_writeout_enabled)) { - g_cond_signal(&trace_empty_cond); - g_cond_wait(&trace_available_cond, &trace_lock); - } - trace_available = false; - g_mutex_unlock(&trace_lock); -} - -static gpointer writeout_thread(gpointer opaque) -{ - TraceRecord *recordptr; - union { - TraceRecord rec; - uint8_t bytes[sizeof(TraceRecord) + sizeof(uint64_t)]; - } dropped; - unsigned int idx = 0; - int dropped_count; - size_t unused __attribute__ ((unused)); - - for (;;) { - wait_for_trace_records_available(); - - if (g_atomic_int_get(&dropped_events)) { - dropped.rec.event = DROPPED_EVENT_ID, - dropped.rec.timestamp_ns = get_clock(); - dropped.rec.length = sizeof(TraceRecord) + sizeof(uint64_t), - dropped.rec.pid = trace_pid; - do { - dropped_count = g_atomic_int_get(&dropped_events); - } while (!g_atomic_int_compare_and_exchange(&dropped_events, - dropped_count, 0)); - dropped.rec.arguments[0] = dropped_count; - unused = fwrite(&dropped.rec, dropped.rec.length, 1, trace_fp); - } - - while (get_trace_record(idx, &recordptr)) { - unused = fwrite(recordptr, recordptr->length, 1, trace_fp); - writeout_idx += recordptr->length; - free(recordptr); /* dont use g_free, can deadlock when traced */ - idx = writeout_idx % TRACE_BUF_LEN; - } - - fflush(trace_fp); - } - return NULL; -} - -void trace_record_write_u64(TraceBufferRecord *rec, uint64_t val) -{ - rec->rec_off = write_to_buffer(rec->rec_off, &val, sizeof(uint64_t)); -} - -void trace_record_write_str(TraceBufferRecord *rec, const char *s, uint32_t slen) -{ - /* Write string length first */ - rec->rec_off = write_to_buffer(rec->rec_off, &slen, sizeof(slen)); - /* Write actual string now */ - rec->rec_off = write_to_buffer(rec->rec_off, (void*)s, slen); -} - -int trace_record_start(TraceBufferRecord *rec, TraceEventID event, size_t datasize) -{ - unsigned int idx, rec_off, old_idx, new_idx; - uint32_t rec_len = sizeof(TraceRecord) + datasize; - uint64_t event_u64 = event; - uint64_t timestamp_ns = get_clock(); - - do { - old_idx = g_atomic_int_get(&trace_idx); - smp_rmb(); - new_idx = old_idx + rec_len; - - if (new_idx - writeout_idx > TRACE_BUF_LEN) { - /* Trace Buffer Full, Event dropped ! */ - g_atomic_int_inc(&dropped_events); - return -ENOSPC; - } - } while (!g_atomic_int_compare_and_exchange(&trace_idx, old_idx, new_idx)); - - idx = old_idx % TRACE_BUF_LEN; - - rec_off = idx; - rec_off = write_to_buffer(rec_off, &event_u64, sizeof(event_u64)); - rec_off = write_to_buffer(rec_off, ×tamp_ns, sizeof(timestamp_ns)); - rec_off = write_to_buffer(rec_off, &rec_len, sizeof(rec_len)); - rec_off = write_to_buffer(rec_off, &trace_pid, sizeof(trace_pid)); - - rec->tbuf_idx = idx; - rec->rec_off = (idx + sizeof(TraceRecord)) % TRACE_BUF_LEN; - return 0; -} - -static void read_from_buffer(unsigned int idx, void *dataptr, size_t size) -{ - uint8_t *data_ptr = dataptr; - uint32_t x = 0; - while (x < size) { - if (idx >= TRACE_BUF_LEN) { - idx = idx % TRACE_BUF_LEN; - } - data_ptr[x++] = trace_buf[idx++]; - } -} - -static unsigned int write_to_buffer(unsigned int idx, void *dataptr, size_t size) -{ - uint8_t *data_ptr = dataptr; - uint32_t x = 0; - while (x < size) { - if (idx >= TRACE_BUF_LEN) { - idx = idx % TRACE_BUF_LEN; - } - trace_buf[idx++] = data_ptr[x++]; - } - return idx; /* most callers wants to know where to write next */ -} - -void trace_record_finish(TraceBufferRecord *rec) -{ - TraceRecord record; - read_from_buffer(rec->tbuf_idx, &record, sizeof(TraceRecord)); - smp_wmb(); /* write barrier before marking as valid */ - record.event |= TRACE_RECORD_VALID; - write_to_buffer(rec->tbuf_idx, &record, sizeof(TraceRecord)); - - if (((unsigned int)g_atomic_int_get(&trace_idx) - writeout_idx) - > TRACE_BUF_FLUSH_THRESHOLD) { - flush_trace_file(false); - } -} - -void st_set_trace_file_enabled(bool enable) -{ - if (enable == !!trace_fp) { - return; /* no change */ - } - - /* Halt trace writeout */ - flush_trace_file(true); - trace_writeout_enabled = false; - flush_trace_file(true); - - if (enable) { - static const TraceLogHeader header = { - .header_event_id = HEADER_EVENT_ID, - .header_magic = HEADER_MAGIC, - /* Older log readers will check for version at next location */ - .header_version = HEADER_VERSION, - }; - - trace_fp = fopen(trace_file_name, "wb"); - if (!trace_fp) { - return; - } - - if (fwrite(&header, sizeof header, 1, trace_fp) != 1) { - fclose(trace_fp); - trace_fp = NULL; - return; - } - - /* Resume trace writeout */ - trace_writeout_enabled = true; - flush_trace_file(false); - } else { - fclose(trace_fp); - trace_fp = NULL; - } -} - -/** - * Set the name of a trace file - * - * @file The trace file name or NULL for the default name-<pid> set at - * config time - */ -void st_set_trace_file(const char *file) -{ - st_set_trace_file_enabled(false); - - g_free(trace_file_name); - - if (!file) { - /* Type cast needed for Windows where getpid() returns an int. */ - trace_file_name = g_strdup_printf(CONFIG_TRACE_FILE, (pid_t)getpid()); - } else { - trace_file_name = g_strdup_printf("%s", file); - } - - st_set_trace_file_enabled(true); -} - -void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) -{ - stream_printf(stream, "Trace file \"%s\" %s.\n", - trace_file_name, trace_fp ? "on" : "off"); -} - -void st_flush_trace_buffer(void) -{ - flush_trace_file(true); -} - -/* Helper function to create a thread with signals blocked. Use glib's - * portable threads since QEMU abstractions cannot be used due to reentrancy in - * the tracer. Also note the signal masking on POSIX hosts so that the thread - * does not steal signals when the rest of the program wants them blocked. - */ -static GThread *trace_thread_create(GThreadFunc fn) -{ - GThread *thread; -#ifndef _WIN32 - sigset_t set, oldset; - - sigfillset(&set); - pthread_sigmask(SIG_SETMASK, &set, &oldset); -#endif - - thread = g_thread_new("trace-thread", fn, NULL); - -#ifndef _WIN32 - pthread_sigmask(SIG_SETMASK, &oldset, NULL); -#endif - - return thread; -} - -bool st_init(void) -{ - GThread *thread; - - trace_pid = getpid(); - - thread = trace_thread_create(writeout_thread); - if (!thread) { - fprintf(stderr, "warning: unable to initialize simple trace backend\n"); - return false; - } - - atexit(st_flush_trace_buffer); - return true; -} |