Statistics
| Branch: | Revision:

root / simpletrace.c @ f871d689

History | View | Annotate | Download (6.1 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 "trace.h"
16

    
17
/** Trace file header event ID */
18
#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
19

    
20
/** Trace file magic number */
21
#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
22

    
23
/** Trace file version number, bump if format changes */
24
#define HEADER_VERSION 0
25

    
26
/** Trace buffer entry */
27
typedef struct {
28
    uint64_t event;
29
    uint64_t timestamp_ns;
30
    uint64_t x1;
31
    uint64_t x2;
32
    uint64_t x3;
33
    uint64_t x4;
34
    uint64_t x5;
35
    uint64_t x6;
36
} TraceRecord;
37

    
38
enum {
39
    TRACE_BUF_LEN = 64 * 1024 / sizeof(TraceRecord),
40
};
41

    
42
static TraceRecord trace_buf[TRACE_BUF_LEN];
43
static unsigned int trace_idx;
44
static FILE *trace_fp;
45
static char *trace_file_name = NULL;
46
static bool trace_file_enabled = false;
47

    
48
void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
49
{
50
    stream_printf(stream, "Trace file \"%s\" %s.\n",
51
                  trace_file_name, trace_file_enabled ? "on" : "off");
52
}
53

    
54
static bool write_header(FILE *fp)
55
{
56
    static const TraceRecord header = {
57
        .event = HEADER_EVENT_ID,
58
        .timestamp_ns = HEADER_MAGIC,
59
        .x1 = HEADER_VERSION,
60
    };
61

    
62
    return fwrite(&header, sizeof header, 1, fp) == 1;
63
}
64

    
65
/**
66
 * set_trace_file : To set the name of a trace file.
67
 * @file : pointer to the name to be set.
68
 *         If NULL, set to the default name-<pid> set at config time.
69
 */
70
bool st_set_trace_file(const char *file)
71
{
72
    st_set_trace_file_enabled(false);
73

    
74
    free(trace_file_name);
75

    
76
    if (!file) {
77
        if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) {
78
            trace_file_name = NULL;
79
            return false;
80
        }
81
    } else {
82
        if (asprintf(&trace_file_name, "%s", file) < 0) {
83
            trace_file_name = NULL;
84
            return false;
85
        }
86
    }
87

    
88
    st_set_trace_file_enabled(true);
89
    return true;
90
}
91

    
92
static void flush_trace_file(void)
93
{
94
    /* If the trace file is not open yet, open it now */
95
    if (!trace_fp) {
96
        trace_fp = fopen(trace_file_name, "w");
97
        if (!trace_fp) {
98
            /* Avoid repeatedly trying to open file on failure */
99
            trace_file_enabled = false;
100
            return;
101
        }
102
        write_header(trace_fp);
103
    }
104

    
105
    if (trace_fp) {
106
        size_t unused; /* for when fwrite(3) is declared warn_unused_result */
107
        unused = fwrite(trace_buf, trace_idx * sizeof(trace_buf[0]), 1, trace_fp);
108
    }
109
}
110

    
111
void st_flush_trace_buffer(void)
112
{
113
    if (trace_file_enabled) {
114
        flush_trace_file();
115
    }
116

    
117
    /* Discard written trace records */
118
    trace_idx = 0;
119
}
120

    
121
void st_set_trace_file_enabled(bool enable)
122
{
123
    if (enable == trace_file_enabled) {
124
        return; /* no change */
125
    }
126

    
127
    /* Flush/discard trace buffer */
128
    st_flush_trace_buffer();
129

    
130
    /* To disable, close trace file */
131
    if (!enable) {
132
        fclose(trace_fp);
133
        trace_fp = NULL;
134
    }
135

    
136
    trace_file_enabled = enable;
137
}
138

    
139
static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
140
                  uint64_t x4, uint64_t x5, uint64_t x6)
141
{
142
    TraceRecord *rec = &trace_buf[trace_idx];
143
    struct timespec ts;
144

    
145
    /* TODO Windows?  It would be good to use qemu-timer here but that isn't
146
     * linked into qemu-tools.  Also we should avoid recursion in the tracing
147
     * code, therefore it is useful to be self-contained.
148
     */
149
    clock_gettime(CLOCK_MONOTONIC, &ts);
150

    
151
    if (!trace_list[event].state) {
152
        return;
153
    }
154

    
155
    rec->event = event;
156
    rec->timestamp_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;
157
    rec->x1 = x1;
158
    rec->x2 = x2;
159
    rec->x3 = x3;
160
    rec->x4 = x4;
161
    rec->x5 = x5;
162
    rec->x6 = x6;
163

    
164
    if (++trace_idx == TRACE_BUF_LEN) {
165
        st_flush_trace_buffer();
166
    }
167
}
168

    
169
void trace0(TraceEventID event)
170
{
171
    trace(event, 0, 0, 0, 0, 0, 0);
172
}
173

    
174
void trace1(TraceEventID event, uint64_t x1)
175
{
176
    trace(event, x1, 0, 0, 0, 0, 0);
177
}
178

    
179
void trace2(TraceEventID event, uint64_t x1, uint64_t x2)
180
{
181
    trace(event, x1, x2, 0, 0, 0, 0);
182
}
183

    
184
void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3)
185
{
186
    trace(event, x1, x2, x3, 0, 0, 0);
187
}
188

    
189
void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4)
190
{
191
    trace(event, x1, x2, x3, x4, 0, 0);
192
}
193

    
194
void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5)
195
{
196
    trace(event, x1, x2, x3, x4, x5, 0);
197
}
198

    
199
void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6)
200
{
201
    trace(event, x1, x2, x3, x4, x5, x6);
202
}
203

    
204
/**
205
 * Flush the trace buffer on exit
206
 */
207
static void __attribute__((constructor)) st_init(void)
208
{
209
    atexit(st_flush_trace_buffer);
210
}
211

    
212
void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
213
{
214
    unsigned int i;
215

    
216
    for (i = 0; i < trace_idx; i++) {
217
        stream_printf(stream, "Event %lu : %lx %lx %lx %lx %lx\n",
218
                      trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2,
219
                      trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5);
220
    }
221
}
222

    
223
void st_print_trace_events(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
224
{
225
    unsigned int i;
226

    
227
    for (i = 0; i < NR_TRACE_EVENTS; i++) {
228
        stream_printf(stream, "%s [Event ID %u] : state %u\n",
229
                      trace_list[i].tp_name, i, trace_list[i].state);
230
    }
231
}
232

    
233
static TraceEvent* find_trace_event_by_name(const char *tname)
234
{
235
    unsigned int i;
236

    
237
    if (!tname) {
238
        return NULL;
239
    }
240

    
241
    for (i = 0; i < NR_TRACE_EVENTS; i++) {
242
        if (!strcmp(trace_list[i].tp_name, tname)) {
243
            return &trace_list[i];
244
        }
245
    }
246
    return NULL; /* indicates end of list reached without a match */
247
}
248

    
249
bool st_change_trace_event_state(const char *tname, bool tstate)
250
{
251
    TraceEvent *tp;
252

    
253
    tp = find_trace_event_by_name(tname);
254
    if (tp) {
255
        tp->state = tstate;
256
        return true;
257
    }
258
    return false;
259
}