4 * Copyright IBM, Corp. 2010
6 * This work is licensed under the terms of the GNU GPL, version 2. See
7 * the COPYING file in the top-level directory.
17 #include "qemu-timer.h"
20 /** Trace file header event ID */
21 #define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
23 /** Trace file magic number */
24 #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
26 /** Trace file version number, bump if format changes */
27 #define HEADER_VERSION 0
29 /** Records were dropped event ID */
30 #define DROPPED_EVENT_ID (~(uint64_t)0 - 1)
32 /** Trace record is valid */
33 #define TRACE_RECORD_VALID ((uint64_t)1 << 63)
35 /** Trace buffer entry */
38 uint64_t timestamp_ns
;
49 TRACE_BUF_FLUSH_THRESHOLD
= TRACE_BUF_LEN
/ 4,
53 * Trace records are written out by a dedicated thread. The thread waits for
54 * records to become available, writes them out, and then waits again.
56 static pthread_mutex_t trace_lock
= PTHREAD_MUTEX_INITIALIZER
;
57 static pthread_cond_t trace_available_cond
= PTHREAD_COND_INITIALIZER
;
58 static pthread_cond_t trace_empty_cond
= PTHREAD_COND_INITIALIZER
;
59 static bool trace_available
;
60 static bool trace_writeout_enabled
;
62 static TraceRecord trace_buf
[TRACE_BUF_LEN
];
63 static unsigned int trace_idx
;
64 static FILE *trace_fp
;
65 static char *trace_file_name
= NULL
;
68 * Read a trace record from the trace buffer
70 * @idx Trace buffer index
71 * @record Trace record to fill
73 * Returns false if the record is not valid.
75 static bool get_trace_record(unsigned int idx
, TraceRecord
*record
)
77 if (!(trace_buf
[idx
].event
& TRACE_RECORD_VALID
)) {
81 __sync_synchronize(); /* read memory barrier before accessing record */
83 *record
= trace_buf
[idx
];
84 record
->event
&= ~TRACE_RECORD_VALID
;
89 * Kick writeout thread
91 * @wait Whether to wait for writeout thread to complete
93 static void flush_trace_file(bool wait
)
95 pthread_mutex_lock(&trace_lock
);
96 trace_available
= true;
97 pthread_cond_signal(&trace_available_cond
);
100 pthread_cond_wait(&trace_empty_cond
, &trace_lock
);
103 pthread_mutex_unlock(&trace_lock
);
106 static void wait_for_trace_records_available(void)
108 pthread_mutex_lock(&trace_lock
);
109 while (!(trace_available
&& trace_writeout_enabled
)) {
110 pthread_cond_signal(&trace_empty_cond
);
111 pthread_cond_wait(&trace_available_cond
, &trace_lock
);
113 trace_available
= false;
114 pthread_mutex_unlock(&trace_lock
);
117 static void *writeout_thread(void *opaque
)
120 unsigned int writeout_idx
= 0;
121 unsigned int num_available
, idx
;
125 wait_for_trace_records_available();
127 num_available
= trace_idx
- writeout_idx
;
128 if (num_available
> TRACE_BUF_LEN
) {
129 record
= (TraceRecord
){
130 .event
= DROPPED_EVENT_ID
,
133 unused
= fwrite(&record
, sizeof(record
), 1, trace_fp
);
134 writeout_idx
+= num_available
;
137 idx
= writeout_idx
% TRACE_BUF_LEN
;
138 while (get_trace_record(idx
, &record
)) {
139 trace_buf
[idx
].event
= 0; /* clear valid bit */
140 unused
= fwrite(&record
, sizeof(record
), 1, trace_fp
);
141 idx
= ++writeout_idx
% TRACE_BUF_LEN
;
149 static void trace(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
,
150 uint64_t x4
, uint64_t x5
, uint64_t x6
)
155 if (!trace_list
[event
].state
) {
159 timestamp
= get_clock();
161 idx
= __sync_fetch_and_add(&trace_idx
, 1) % TRACE_BUF_LEN
;
162 trace_buf
[idx
] = (TraceRecord
){
164 .timestamp_ns
= timestamp
,
172 __sync_synchronize(); /* write barrier before marking as valid */
173 trace_buf
[idx
].event
|= TRACE_RECORD_VALID
;
175 if ((idx
+ 1) % TRACE_BUF_FLUSH_THRESHOLD
== 0) {
176 flush_trace_file(false);
180 void trace0(TraceEventID event
)
182 trace(event
, 0, 0, 0, 0, 0, 0);
185 void trace1(TraceEventID event
, uint64_t x1
)
187 trace(event
, x1
, 0, 0, 0, 0, 0);
190 void trace2(TraceEventID event
, uint64_t x1
, uint64_t x2
)
192 trace(event
, x1
, x2
, 0, 0, 0, 0);
195 void trace3(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
)
197 trace(event
, x1
, x2
, x3
, 0, 0, 0);
200 void trace4(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
)
202 trace(event
, x1
, x2
, x3
, x4
, 0, 0);
205 void trace5(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
, uint64_t x5
)
207 trace(event
, x1
, x2
, x3
, x4
, x5
, 0);
210 void trace6(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
, uint64_t x5
, uint64_t x6
)
212 trace(event
, x1
, x2
, x3
, x4
, x5
, x6
);
215 void st_set_trace_file_enabled(bool enable
)
217 if (enable
== !!trace_fp
) {
218 return; /* no change */
221 /* Halt trace writeout */
222 flush_trace_file(true);
223 trace_writeout_enabled
= false;
224 flush_trace_file(true);
227 static const TraceRecord header
= {
228 .event
= HEADER_EVENT_ID
,
229 .timestamp_ns
= HEADER_MAGIC
,
230 .x1
= HEADER_VERSION
,
233 trace_fp
= fopen(trace_file_name
, "w");
238 if (fwrite(&header
, sizeof header
, 1, trace_fp
) != 1) {
244 /* Resume trace writeout */
245 trace_writeout_enabled
= true;
246 flush_trace_file(false);
254 * Set the name of a trace file
256 * @file The trace file name or NULL for the default name-<pid> set at
259 bool st_set_trace_file(const char *file
)
261 st_set_trace_file_enabled(false);
263 free(trace_file_name
);
266 if (asprintf(&trace_file_name
, CONFIG_TRACE_FILE
, getpid()) < 0) {
267 trace_file_name
= NULL
;
271 if (asprintf(&trace_file_name
, "%s", file
) < 0) {
272 trace_file_name
= NULL
;
277 st_set_trace_file_enabled(true);
281 void st_print_trace_file_status(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
283 stream_printf(stream
, "Trace file \"%s\" %s.\n",
284 trace_file_name
, trace_fp
? "on" : "off");
287 void st_print_trace(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
291 for (i
= 0; i
< TRACE_BUF_LEN
; i
++) {
294 if (!get_trace_record(i
, &record
)) {
297 stream_printf(stream
, "Event %" PRIu64
" : %" PRIx64
" %" PRIx64
298 " %" PRIx64
" %" PRIx64
" %" PRIx64
" %" PRIx64
"\n",
299 record
.event
, record
.x1
, record
.x2
,
300 record
.x3
, record
.x4
, record
.x5
,
305 void st_print_trace_events(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
309 for (i
= 0; i
< NR_TRACE_EVENTS
; i
++) {
310 stream_printf(stream
, "%s [Event ID %u] : state %u\n",
311 trace_list
[i
].tp_name
, i
, trace_list
[i
].state
);
315 bool st_change_trace_event_state(const char *name
, bool enabled
)
319 for (i
= 0; i
< NR_TRACE_EVENTS
; i
++) {
320 if (!strcmp(trace_list
[i
].tp_name
, name
)) {
321 trace_list
[i
].state
= enabled
;
328 void st_flush_trace_buffer(void)
330 flush_trace_file(true);
333 bool st_init(const char *file
)
337 sigset_t set
, oldset
;
340 pthread_attr_init(&attr
);
341 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
344 pthread_sigmask(SIG_SETMASK
, &set
, &oldset
);
345 ret
= pthread_create(&thread
, &attr
, writeout_thread
, NULL
);
346 pthread_sigmask(SIG_SETMASK
, &oldset
, NULL
);
352 atexit(st_flush_trace_buffer
);
353 st_set_trace_file(file
);