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.
18 #include "qemu-timer.h"
21 /** Trace file header event ID */
22 #define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
24 /** Trace file magic number */
25 #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
27 /** Trace file version number, bump if format changes */
28 #define HEADER_VERSION 0
30 /** Records were dropped event ID */
31 #define DROPPED_EVENT_ID (~(uint64_t)0 - 1)
33 /** Trace record is valid */
34 #define TRACE_RECORD_VALID ((uint64_t)1 << 63)
36 /** Trace buffer entry */
39 uint64_t timestamp_ns
;
50 TRACE_BUF_FLUSH_THRESHOLD
= TRACE_BUF_LEN
/ 4,
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.
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
;
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
;
69 * Read a trace record from the trace buffer
71 * @idx Trace buffer index
72 * @record Trace record to fill
74 * Returns false if the record is not valid.
76 static bool get_trace_record(unsigned int idx
, TraceRecord
*record
)
78 if (!(trace_buf
[idx
].event
& TRACE_RECORD_VALID
)) {
82 __sync_synchronize(); /* read memory barrier before accessing record */
84 *record
= trace_buf
[idx
];
85 record
->event
&= ~TRACE_RECORD_VALID
;
90 * Kick writeout thread
92 * @wait Whether to wait for writeout thread to complete
94 static void flush_trace_file(bool wait
)
96 pthread_mutex_lock(&trace_lock
);
97 trace_available
= true;
98 pthread_cond_signal(&trace_available_cond
);
101 pthread_cond_wait(&trace_empty_cond
, &trace_lock
);
104 pthread_mutex_unlock(&trace_lock
);
107 static void wait_for_trace_records_available(void)
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
);
114 trace_available
= false;
115 pthread_mutex_unlock(&trace_lock
);
118 static void *writeout_thread(void *opaque
)
121 unsigned int writeout_idx
= 0;
122 unsigned int num_available
, idx
;
126 wait_for_trace_records_available();
128 num_available
= trace_idx
- writeout_idx
;
129 if (num_available
> TRACE_BUF_LEN
) {
130 record
= (TraceRecord
){
131 .event
= DROPPED_EVENT_ID
,
134 unused
= fwrite(&record
, sizeof(record
), 1, trace_fp
);
135 writeout_idx
+= num_available
;
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
;
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
)
156 if (!trace_list
[event
].state
) {
160 timestamp
= get_clock();
162 idx
= __sync_fetch_and_add(&trace_idx
, 1) % TRACE_BUF_LEN
;
163 trace_buf
[idx
] = (TraceRecord
){
165 .timestamp_ns
= timestamp
,
173 __sync_synchronize(); /* write barrier before marking as valid */
174 trace_buf
[idx
].event
|= TRACE_RECORD_VALID
;
176 if ((idx
+ 1) % TRACE_BUF_FLUSH_THRESHOLD
== 0) {
177 flush_trace_file(false);
181 void trace0(TraceEventID event
)
183 trace(event
, 0, 0, 0, 0, 0, 0);
186 void trace1(TraceEventID event
, uint64_t x1
)
188 trace(event
, x1
, 0, 0, 0, 0, 0);
191 void trace2(TraceEventID event
, uint64_t x1
, uint64_t x2
)
193 trace(event
, x1
, x2
, 0, 0, 0, 0);
196 void trace3(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
)
198 trace(event
, x1
, x2
, x3
, 0, 0, 0);
201 void trace4(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
)
203 trace(event
, x1
, x2
, x3
, x4
, 0, 0);
206 void trace5(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
, uint64_t x5
)
208 trace(event
, x1
, x2
, x3
, x4
, x5
, 0);
211 void trace6(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
, uint64_t x5
, uint64_t x6
)
213 trace(event
, x1
, x2
, x3
, x4
, x5
, x6
);
216 void st_set_trace_file_enabled(bool enable
)
218 if (enable
== !!trace_fp
) {
219 return; /* no change */
222 /* Halt trace writeout */
223 flush_trace_file(true);
224 trace_writeout_enabled
= false;
225 flush_trace_file(true);
228 static const TraceRecord header
= {
229 .event
= HEADER_EVENT_ID
,
230 .timestamp_ns
= HEADER_MAGIC
,
231 .x1
= HEADER_VERSION
,
234 trace_fp
= fopen(trace_file_name
, "w");
239 if (fwrite(&header
, sizeof header
, 1, trace_fp
) != 1) {
245 /* Resume trace writeout */
246 trace_writeout_enabled
= true;
247 flush_trace_file(false);
255 * Set the name of a trace file
257 * @file The trace file name or NULL for the default name-<pid> set at
260 bool st_set_trace_file(const char *file
)
262 st_set_trace_file_enabled(false);
264 free(trace_file_name
);
267 if (asprintf(&trace_file_name
, CONFIG_TRACE_FILE
, getpid()) < 0) {
268 trace_file_name
= NULL
;
272 if (asprintf(&trace_file_name
, "%s", file
) < 0) {
273 trace_file_name
= NULL
;
278 st_set_trace_file_enabled(true);
282 void st_print_trace_file_status(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
284 stream_printf(stream
, "Trace file \"%s\" %s.\n",
285 trace_file_name
, trace_fp
? "on" : "off");
288 void st_print_trace(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
292 for (i
= 0; i
< TRACE_BUF_LEN
; i
++) {
295 if (!get_trace_record(i
, &record
)) {
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
,
306 void st_print_trace_events(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
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
);
316 bool st_change_trace_event_state(const char *name
, bool enabled
)
320 for (i
= 0; i
< NR_TRACE_EVENTS
; i
++) {
321 if (!strcmp(trace_list
[i
].tp_name
, name
)) {
322 trace_list
[i
].state
= enabled
;
329 void st_flush_trace_buffer(void)
331 flush_trace_file(true);
334 void st_init(const char *file
)
338 sigset_t set
, oldset
;
341 pthread_attr_init(&attr
);
342 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
345 pthread_sigmask(SIG_SETMASK
, &set
, &oldset
);
346 ret
= pthread_create(&thread
, &attr
, writeout_thread
, NULL
);
347 pthread_sigmask(SIG_SETMASK
, &oldset
, NULL
);
350 error_report("warning: unable to create trace file thread\n");
354 atexit(st_flush_trace_buffer
);
355 st_set_trace_file(file
);