Merge remote-tracking branch 'stefanha/tracing' into staging
This commit is contained in:
commit
bea09f657f
@ -459,6 +459,12 @@ S: Maintained
|
||||
F: slirp/
|
||||
T: git://git.kiszka.org/qemu.git queues/slirp
|
||||
|
||||
Tracing
|
||||
M: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
||||
S: Maintained
|
||||
F: trace/
|
||||
T: git://repo.or.cz/qemu/stefanha.git tracing
|
||||
|
||||
Usermode Emulation
|
||||
------------------
|
||||
BSD user
|
||||
|
@ -31,8 +31,8 @@ There is a set of static trace events declared in the "trace-events" source
|
||||
file. Each trace event declaration names the event, its arguments, and the
|
||||
format string which can be used for pretty-printing:
|
||||
|
||||
qemu_malloc(size_t size, void *ptr) "size %zu ptr %p"
|
||||
qemu_free(void *ptr) "ptr %p"
|
||||
qemu_vmalloc(size_t size, void *ptr) "size %zu ptr %p"
|
||||
qemu_vfree(void *ptr) "ptr %p"
|
||||
|
||||
The "trace-events" file is processed by the "tracetool" script during build to
|
||||
generate code for the trace events. Trace events are invoked directly from
|
||||
@ -40,14 +40,16 @@ source code like this:
|
||||
|
||||
#include "trace.h" /* needed for trace event prototype */
|
||||
|
||||
void *qemu_malloc(size_t size)
|
||||
void *qemu_vmalloc(size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
if (!size && !allow_zero_malloc()) {
|
||||
abort();
|
||||
size_t align = QEMU_VMALLOC_ALIGN;
|
||||
|
||||
if (size < align) {
|
||||
align = getpagesize();
|
||||
}
|
||||
ptr = oom_check(malloc(size ? size : 1));
|
||||
trace_qemu_malloc(size, ptr); /* <-- trace event */
|
||||
ptr = qemu_memalign(align, size);
|
||||
trace_qemu_vmalloc(size, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@ -70,11 +72,6 @@ Trace events should use types as follows:
|
||||
cannot include all user-defined struct declarations and it is therefore
|
||||
necessary to use void * for pointers to structs.
|
||||
|
||||
Pointers (including char *) cannot be dereferenced easily (or at all) in
|
||||
some trace backends. If pointers are used, ensure they are meaningful by
|
||||
themselves and do not assume the data they point to will be traced. Do
|
||||
not pass in string arguments.
|
||||
|
||||
* For everything else, use primitive scalar types (char, int, long) with the
|
||||
appropriate signedness.
|
||||
|
||||
@ -182,6 +179,9 @@ source tree. It may not be as powerful as platform-specific or third-party
|
||||
trace backends but it is portable. This is the recommended trace backend
|
||||
unless you have specific needs for more advanced backends.
|
||||
|
||||
The "simple" backend currently does not capture string arguments, it simply
|
||||
records the char* pointer value instead of the string that is pointed to.
|
||||
|
||||
==== Monitor commands ====
|
||||
|
||||
* info trace
|
||||
|
@ -12,8 +12,10 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#ifndef _WIN32
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
#include "qemu-timer.h"
|
||||
#include "trace.h"
|
||||
#include "trace/control.h"
|
||||
@ -54,9 +56,9 @@ enum {
|
||||
* 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 pthread_mutex_t trace_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t trace_available_cond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_cond_t trace_empty_cond = PTHREAD_COND_INITIALIZER;
|
||||
static GStaticMutex trace_lock = G_STATIC_MUTEX_INIT;
|
||||
static GCond *trace_available_cond;
|
||||
static GCond *trace_empty_cond;
|
||||
static bool trace_available;
|
||||
static bool trace_writeout_enabled;
|
||||
|
||||
@ -93,29 +95,30 @@ static bool get_trace_record(unsigned int idx, TraceRecord *record)
|
||||
*/
|
||||
static void flush_trace_file(bool wait)
|
||||
{
|
||||
pthread_mutex_lock(&trace_lock);
|
||||
g_static_mutex_lock(&trace_lock);
|
||||
trace_available = true;
|
||||
pthread_cond_signal(&trace_available_cond);
|
||||
g_cond_signal(trace_available_cond);
|
||||
|
||||
if (wait) {
|
||||
pthread_cond_wait(&trace_empty_cond, &trace_lock);
|
||||
g_cond_wait(trace_empty_cond, g_static_mutex_get_mutex(&trace_lock));
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&trace_lock);
|
||||
g_static_mutex_unlock(&trace_lock);
|
||||
}
|
||||
|
||||
static void wait_for_trace_records_available(void)
|
||||
{
|
||||
pthread_mutex_lock(&trace_lock);
|
||||
g_static_mutex_lock(&trace_lock);
|
||||
while (!(trace_available && trace_writeout_enabled)) {
|
||||
pthread_cond_signal(&trace_empty_cond);
|
||||
pthread_cond_wait(&trace_available_cond, &trace_lock);
|
||||
g_cond_signal(trace_empty_cond);
|
||||
g_cond_wait(trace_available_cond,
|
||||
g_static_mutex_get_mutex(&trace_lock));
|
||||
}
|
||||
trace_available = false;
|
||||
pthread_mutex_unlock(&trace_lock);
|
||||
g_static_mutex_unlock(&trace_lock);
|
||||
}
|
||||
|
||||
static void *writeout_thread(void *opaque)
|
||||
static gpointer writeout_thread(gpointer opaque)
|
||||
{
|
||||
TraceRecord record;
|
||||
unsigned int writeout_idx = 0;
|
||||
@ -159,7 +162,7 @@ static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
|
||||
|
||||
timestamp = get_clock();
|
||||
|
||||
idx = __sync_fetch_and_add(&trace_idx, 1) % TRACE_BUF_LEN;
|
||||
idx = g_atomic_int_exchange_and_add((gint *)&trace_idx, 1) % TRACE_BUF_LEN;
|
||||
trace_buf[idx] = (TraceRecord){
|
||||
.event = event,
|
||||
.timestamp_ns = timestamp,
|
||||
@ -231,7 +234,7 @@ void st_set_trace_file_enabled(bool enable)
|
||||
.x1 = HEADER_VERSION,
|
||||
};
|
||||
|
||||
trace_fp = fopen(trace_file_name, "w");
|
||||
trace_fp = fopen(trace_file_name, "wb");
|
||||
if (!trace_fp) {
|
||||
return;
|
||||
}
|
||||
@ -331,28 +334,47 @@ bool trace_event_set_state(const char *name, bool state)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool trace_backend_init(const char *events, const char *file)
|
||||
/* 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)
|
||||
{
|
||||
pthread_t thread;
|
||||
pthread_attr_t attr;
|
||||
GThread *thread;
|
||||
#ifndef _WIN32
|
||||
sigset_t set, oldset;
|
||||
int ret;
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
sigfillset(&set);
|
||||
pthread_sigmask(SIG_SETMASK, &set, &oldset);
|
||||
ret = pthread_create(&thread, &attr, writeout_thread, NULL);
|
||||
#endif
|
||||
thread = g_thread_create(writeout_thread, NULL, FALSE, NULL);
|
||||
#ifndef _WIN32
|
||||
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
|
||||
#endif
|
||||
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "warning: unable to initialize simple trace backend\n");
|
||||
} else {
|
||||
atexit(st_flush_trace_buffer);
|
||||
trace_backend_init_events(events);
|
||||
st_set_trace_file(file);
|
||||
return thread;
|
||||
}
|
||||
|
||||
bool trace_backend_init(const char *events, const char *file)
|
||||
{
|
||||
GThread *thread;
|
||||
|
||||
if (!g_thread_supported()) {
|
||||
g_thread_init(NULL);
|
||||
}
|
||||
|
||||
trace_available_cond = g_cond_new();
|
||||
trace_empty_cond = g_cond_new();
|
||||
|
||||
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);
|
||||
trace_backend_init_events(events);
|
||||
st_set_trace_file(file);
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user