Statistics
| Branch: | Revision:

root / simpletrace.c @ 3c85e74f

History | View | Annotate | Download (8.7 kB)

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
#include <signal.h>
16
#include <pthread.h>
17
#include "qerror.h"
18
#include "qemu-timer.h"
19
#include "trace.h"
20

    
21
/** Trace file header event ID */
22
#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
23

    
24
/** Trace file magic number */
25
#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
26

    
27
/** Trace file version number, bump if format changes */
28
#define HEADER_VERSION 0
29

    
30
/** Records were dropped event ID */
31
#define DROPPED_EVENT_ID (~(uint64_t)0 - 1)
32

    
33
/** Trace record is valid */
34
#define TRACE_RECORD_VALID ((uint64_t)1 << 63)
35

    
36
/** Trace buffer entry */
37
typedef struct {
38
    uint64_t event;
39
    uint64_t timestamp_ns;
40
    uint64_t x1;
41
    uint64_t x2;
42
    uint64_t x3;
43
    uint64_t x4;
44
    uint64_t x5;
45
    uint64_t x6;
46
} TraceRecord;
47

    
48
enum {
49
    TRACE_BUF_LEN = 4096,
50
    TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4,
51
};
52

    
53
/*
54
 * Trace records are written out by a dedicated thread.  The thread waits for
55
 * records to become available, writes them out, and then waits again.
56
 */
57
static pthread_mutex_t trace_lock = PTHREAD_MUTEX_INITIALIZER;
58
static pthread_cond_t trace_available_cond = PTHREAD_COND_INITIALIZER;
59
static pthread_cond_t trace_empty_cond = PTHREAD_COND_INITIALIZER;
60
static bool trace_available;
61
static bool trace_writeout_enabled;
62

    
63
static TraceRecord trace_buf[TRACE_BUF_LEN];
64
static unsigned int trace_idx;
65
static FILE *trace_fp;
66
static char *trace_file_name = NULL;
67

    
68
/**
69
 * Read a trace record from the trace buffer
70
 *
71
 * @idx         Trace buffer index
72
 * @record      Trace record to fill
73
 *
74
 * Returns false if the record is not valid.
75
 */
76
static bool get_trace_record(unsigned int idx, TraceRecord *record)
77
{
78
    if (!(trace_buf[idx].event & TRACE_RECORD_VALID)) {
79
        return false;
80
    }
81

    
82
    __sync_synchronize(); /* read memory barrier before accessing record */
83

    
84
    *record = trace_buf[idx];
85
    record->event &= ~TRACE_RECORD_VALID;
86
    return true;
87
}
88

    
89
/**
90
 * Kick writeout thread
91
 *
92
 * @wait        Whether to wait for writeout thread to complete
93
 */
94
static void flush_trace_file(bool wait)
95
{
96
    pthread_mutex_lock(&trace_lock);
97
    trace_available = true;
98
    pthread_cond_signal(&trace_available_cond);
99

    
100
    if (wait) {
101
        pthread_cond_wait(&trace_empty_cond, &trace_lock);
102
    }
103

    
104
    pthread_mutex_unlock(&trace_lock);
105
}
106

    
107
static void wait_for_trace_records_available(void)
108
{
109
    pthread_mutex_lock(&trace_lock);
110
    while (!(trace_available && trace_writeout_enabled)) {
111
        pthread_cond_signal(&trace_empty_cond);
112
        pthread_cond_wait(&trace_available_cond, &trace_lock);
113
    }
114
    trace_available = false;
115
    pthread_mutex_unlock(&trace_lock);
116
}
117

    
118
static void *writeout_thread(void *opaque)
119
{
120
    TraceRecord record;
121
    unsigned int writeout_idx = 0;
122
    unsigned int num_available, idx;
123
    size_t unused;
124

    
125
    for (;;) {
126
        wait_for_trace_records_available();
127

    
128
        num_available = trace_idx - writeout_idx;
129
        if (num_available > TRACE_BUF_LEN) {
130
            record = (TraceRecord){
131
                .event = DROPPED_EVENT_ID,
132
                .x1 = num_available,
133
            };
134
            unused = fwrite(&record, sizeof(record), 1, trace_fp);
135
            writeout_idx += num_available;
136
        }
137

    
138
        idx = writeout_idx % TRACE_BUF_LEN;
139
        while (get_trace_record(idx, &record)) {
140
            trace_buf[idx].event = 0; /* clear valid bit */
141
            unused = fwrite(&record, sizeof(record), 1, trace_fp);
142
            idx = ++writeout_idx % TRACE_BUF_LEN;
143
        }
144

    
145
        fflush(trace_fp);
146
    }
147
    return NULL;
148
}
149

    
150
static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
151
                  uint64_t x4, uint64_t x5, uint64_t x6)
152
{
153
    unsigned int idx;
154
    uint64_t timestamp;
155

    
156
    if (!trace_list[event].state) {
157
        return;
158
    }
159

    
160
    timestamp = get_clock();
161

    
162
    idx = __sync_fetch_and_add(&trace_idx, 1) % TRACE_BUF_LEN;
163
    trace_buf[idx] = (TraceRecord){
164
        .event = event,
165
        .timestamp_ns = timestamp,
166
        .x1 = x1,
167
        .x2 = x2,
168
        .x3 = x3,
169
        .x4 = x4,
170
        .x5 = x5,
171
        .x6 = x6,
172
    };
173
    __sync_synchronize(); /* write barrier before marking as valid */
174
    trace_buf[idx].event |= TRACE_RECORD_VALID;
175

    
176
    if ((idx + 1) % TRACE_BUF_FLUSH_THRESHOLD == 0) {
177
        flush_trace_file(false);
178
    }
179
}
180

    
181
void trace0(TraceEventID event)
182
{
183
    trace(event, 0, 0, 0, 0, 0, 0);
184
}
185

    
186
void trace1(TraceEventID event, uint64_t x1)
187
{
188
    trace(event, x1, 0, 0, 0, 0, 0);
189
}
190

    
191
void trace2(TraceEventID event, uint64_t x1, uint64_t x2)
192
{
193
    trace(event, x1, x2, 0, 0, 0, 0);
194
}
195

    
196
void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3)
197
{
198
    trace(event, x1, x2, x3, 0, 0, 0);
199
}
200

    
201
void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4)
202
{
203
    trace(event, x1, x2, x3, x4, 0, 0);
204
}
205

    
206
void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5)
207
{
208
    trace(event, x1, x2, x3, x4, x5, 0);
209
}
210

    
211
void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6)
212
{
213
    trace(event, x1, x2, x3, x4, x5, x6);
214
}
215

    
216
void st_set_trace_file_enabled(bool enable)
217
{
218
    if (enable == !!trace_fp) {
219
        return; /* no change */
220
    }
221

    
222
    /* Halt trace writeout */
223
    flush_trace_file(true);
224
    trace_writeout_enabled = false;
225
    flush_trace_file(true);
226

    
227
    if (enable) {
228
        static const TraceRecord header = {
229
            .event = HEADER_EVENT_ID,
230
            .timestamp_ns = HEADER_MAGIC,
231
            .x1 = HEADER_VERSION,
232
        };
233

    
234
        trace_fp = fopen(trace_file_name, "w");
235
        if (!trace_fp) {
236
            return;
237
        }
238

    
239
        if (fwrite(&header, sizeof header, 1, trace_fp) != 1) {
240
            fclose(trace_fp);
241
            trace_fp = NULL;
242
            return;
243
        }
244

    
245
        /* Resume trace writeout */
246
        trace_writeout_enabled = true;
247
        flush_trace_file(false);
248
    } else {
249
        fclose(trace_fp);
250
        trace_fp = NULL;
251
    }
252
}
253

    
254
/**
255
 * Set the name of a trace file
256
 *
257
 * @file        The trace file name or NULL for the default name-<pid> set at
258
 *              config time
259
 */
260
bool st_set_trace_file(const char *file)
261
{
262
    st_set_trace_file_enabled(false);
263

    
264
    free(trace_file_name);
265

    
266
    if (!file) {
267
        if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) {
268
            trace_file_name = NULL;
269
            return false;
270
        }
271
    } else {
272
        if (asprintf(&trace_file_name, "%s", file) < 0) {
273
            trace_file_name = NULL;
274
            return false;
275
        }
276
    }
277

    
278
    st_set_trace_file_enabled(true);
279
    return true;
280
}
281

    
282
void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
283
{
284
    stream_printf(stream, "Trace file \"%s\" %s.\n",
285
                  trace_file_name, trace_fp ? "on" : "off");
286
}
287

    
288
void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
289
{
290
    unsigned int i;
291

    
292
    for (i = 0; i < TRACE_BUF_LEN; i++) {
293
        TraceRecord record;
294

    
295
        if (!get_trace_record(i, &record)) {
296
            continue;
297
        }
298
        stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64
299
                      " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n",
300
                      record.event, record.x1, record.x2,
301
                      record.x3, record.x4, record.x5,
302
                      record.x6);
303
    }
304
}
305

    
306
void st_print_trace_events(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
307
{
308
    unsigned int i;
309

    
310
    for (i = 0; i < NR_TRACE_EVENTS; i++) {
311
        stream_printf(stream, "%s [Event ID %u] : state %u\n",
312
                      trace_list[i].tp_name, i, trace_list[i].state);
313
    }
314
}
315

    
316
bool st_change_trace_event_state(const char *name, bool enabled)
317
{
318
    unsigned int i;
319

    
320
    for (i = 0; i < NR_TRACE_EVENTS; i++) {
321
        if (!strcmp(trace_list[i].tp_name, name)) {
322
            trace_list[i].state = enabled;
323
            return true;
324
        }
325
    }
326
    return false;
327
}
328

    
329
void st_flush_trace_buffer(void)
330
{
331
    flush_trace_file(true);
332
}
333

    
334
void st_init(const char *file)
335
{
336
    pthread_t thread;
337
    pthread_attr_t attr;
338
    sigset_t set, oldset;
339
    int ret;
340

    
341
    pthread_attr_init(&attr);
342
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
343

    
344
    sigfillset(&set);
345
    pthread_sigmask(SIG_SETMASK, &set, &oldset);
346
    ret = pthread_create(&thread, &attr, writeout_thread, NULL);
347
    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
348

    
349
    if (ret != 0) {
350
        error_report("warning: unable to create trace file thread\n");
351
        return;
352
    }
353

    
354
    atexit(st_flush_trace_buffer);
355
    st_set_trace_file(file);
356
}