|
|
1.1 root 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: #ifndef _WIN32
16: #include <signal.h>
17: #include <pthread.h>
18: #endif
19: #include "qemu-timer.h"
20: #include "trace.h"
21: #include "trace/control.h"
22:
23: /** Trace file header event ID */
24: #define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
25:
26: /** Trace file magic number */
27: #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
28:
29: /** Trace file version number, bump if format changes */
30: #define HEADER_VERSION 0
31:
32: /** Records were dropped event ID */
33: #define DROPPED_EVENT_ID (~(uint64_t)0 - 1)
34:
35: /** Trace record is valid */
36: #define TRACE_RECORD_VALID ((uint64_t)1 << 63)
37:
38: /** Trace buffer entry */
39: typedef struct {
40: uint64_t event;
41: uint64_t timestamp_ns;
42: uint64_t x1;
43: uint64_t x2;
44: uint64_t x3;
45: uint64_t x4;
46: uint64_t x5;
47: uint64_t x6;
48: } TraceRecord;
49:
50: enum {
51: TRACE_BUF_LEN = 4096,
52: TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4,
53: };
54:
55: /*
56: * Trace records are written out by a dedicated thread. The thread waits for
57: * records to become available, writes them out, and then waits again.
58: */
59: static GStaticMutex trace_lock = G_STATIC_MUTEX_INIT;
60: static GCond *trace_available_cond;
61: static GCond *trace_empty_cond;
62: static bool trace_available;
63: static bool trace_writeout_enabled;
64:
65: static TraceRecord trace_buf[TRACE_BUF_LEN];
66: static unsigned int trace_idx;
67: static FILE *trace_fp;
68: static char *trace_file_name = NULL;
69:
70: /**
71: * Read a trace record from the trace buffer
72: *
73: * @idx Trace buffer index
74: * @record Trace record to fill
75: *
76: * Returns false if the record is not valid.
77: */
78: static bool get_trace_record(unsigned int idx, TraceRecord *record)
79: {
80: if (!(trace_buf[idx].event & TRACE_RECORD_VALID)) {
81: return false;
82: }
83:
84: __sync_synchronize(); /* read memory barrier before accessing record */
85:
86: *record = trace_buf[idx];
87: record->event &= ~TRACE_RECORD_VALID;
88: return true;
89: }
90:
91: /**
92: * Kick writeout thread
93: *
94: * @wait Whether to wait for writeout thread to complete
95: */
96: static void flush_trace_file(bool wait)
97: {
98: g_static_mutex_lock(&trace_lock);
99: trace_available = true;
100: g_cond_signal(trace_available_cond);
101:
102: if (wait) {
103: g_cond_wait(trace_empty_cond, g_static_mutex_get_mutex(&trace_lock));
104: }
105:
106: g_static_mutex_unlock(&trace_lock);
107: }
108:
109: static void wait_for_trace_records_available(void)
110: {
111: g_static_mutex_lock(&trace_lock);
112: while (!(trace_available && trace_writeout_enabled)) {
113: g_cond_signal(trace_empty_cond);
114: g_cond_wait(trace_available_cond,
115: g_static_mutex_get_mutex(&trace_lock));
116: }
117: trace_available = false;
118: g_static_mutex_unlock(&trace_lock);
119: }
120:
121: static gpointer writeout_thread(gpointer opaque)
122: {
123: TraceRecord record;
124: unsigned int writeout_idx = 0;
125: unsigned int num_available, idx;
126: size_t unused __attribute__ ((unused));
127:
128: for (;;) {
129: wait_for_trace_records_available();
130:
131: num_available = trace_idx - writeout_idx;
132: if (num_available > TRACE_BUF_LEN) {
133: record = (TraceRecord){
134: .event = DROPPED_EVENT_ID,
135: .x1 = num_available,
136: };
137: unused = fwrite(&record, sizeof(record), 1, trace_fp);
138: writeout_idx += num_available;
139: }
140:
141: idx = writeout_idx % TRACE_BUF_LEN;
142: while (get_trace_record(idx, &record)) {
143: trace_buf[idx].event = 0; /* clear valid bit */
144: unused = fwrite(&record, sizeof(record), 1, trace_fp);
145: idx = ++writeout_idx % TRACE_BUF_LEN;
146: }
147:
148: fflush(trace_fp);
149: }
150: return NULL;
151: }
152:
153: static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
154: uint64_t x4, uint64_t x5, uint64_t x6)
155: {
156: unsigned int idx;
157: uint64_t timestamp;
158:
159: if (!trace_list[event].state) {
160: return;
161: }
162:
163: timestamp = get_clock();
164:
165: idx = g_atomic_int_exchange_and_add((gint *)&trace_idx, 1) % TRACE_BUF_LEN;
166: trace_buf[idx] = (TraceRecord){
167: .event = event,
168: .timestamp_ns = timestamp,
169: .x1 = x1,
170: .x2 = x2,
171: .x3 = x3,
172: .x4 = x4,
173: .x5 = x5,
174: .x6 = x6,
175: };
176: __sync_synchronize(); /* write barrier before marking as valid */
177: trace_buf[idx].event |= TRACE_RECORD_VALID;
178:
179: if ((idx + 1) % TRACE_BUF_FLUSH_THRESHOLD == 0) {
180: flush_trace_file(false);
181: }
182: }
183:
184: void trace0(TraceEventID event)
185: {
186: trace(event, 0, 0, 0, 0, 0, 0);
187: }
188:
189: void trace1(TraceEventID event, uint64_t x1)
190: {
191: trace(event, x1, 0, 0, 0, 0, 0);
192: }
193:
194: void trace2(TraceEventID event, uint64_t x1, uint64_t x2)
195: {
196: trace(event, x1, x2, 0, 0, 0, 0);
197: }
198:
199: void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3)
200: {
201: trace(event, x1, x2, x3, 0, 0, 0);
202: }
203:
204: void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4)
205: {
206: trace(event, x1, x2, x3, x4, 0, 0);
207: }
208:
209: void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5)
210: {
211: trace(event, x1, x2, x3, x4, x5, 0);
212: }
213:
214: void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6)
215: {
216: trace(event, x1, x2, x3, x4, x5, x6);
217: }
218:
219: void st_set_trace_file_enabled(bool enable)
220: {
221: if (enable == !!trace_fp) {
222: return; /* no change */
223: }
224:
225: /* Halt trace writeout */
226: flush_trace_file(true);
227: trace_writeout_enabled = false;
228: flush_trace_file(true);
229:
230: if (enable) {
231: static const TraceRecord header = {
232: .event = HEADER_EVENT_ID,
233: .timestamp_ns = HEADER_MAGIC,
234: .x1 = HEADER_VERSION,
235: };
236:
237: trace_fp = fopen(trace_file_name, "wb");
238: if (!trace_fp) {
239: return;
240: }
241:
242: if (fwrite(&header, sizeof header, 1, trace_fp) != 1) {
243: fclose(trace_fp);
244: trace_fp = NULL;
245: return;
246: }
247:
248: /* Resume trace writeout */
249: trace_writeout_enabled = true;
250: flush_trace_file(false);
251: } else {
252: fclose(trace_fp);
253: trace_fp = NULL;
254: }
255: }
256:
257: /**
258: * Set the name of a trace file
259: *
260: * @file The trace file name or NULL for the default name-<pid> set at
261: * config time
262: */
263: bool st_set_trace_file(const char *file)
264: {
265: st_set_trace_file_enabled(false);
266:
267: free(trace_file_name);
268:
269: if (!file) {
270: if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) {
271: trace_file_name = NULL;
272: return false;
273: }
274: } else {
275: if (asprintf(&trace_file_name, "%s", file) < 0) {
276: trace_file_name = NULL;
277: return false;
278: }
279: }
280:
281: st_set_trace_file_enabled(true);
282: return true;
283: }
284:
285: void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
286: {
287: stream_printf(stream, "Trace file \"%s\" %s.\n",
288: trace_file_name, trace_fp ? "on" : "off");
289: }
290:
291: void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
292: {
293: unsigned int i;
294:
295: for (i = 0; i < TRACE_BUF_LEN; i++) {
296: TraceRecord record;
297:
298: if (!get_trace_record(i, &record)) {
299: continue;
300: }
301: stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64
302: " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n",
303: record.event, record.x1, record.x2,
304: record.x3, record.x4, record.x5,
305: record.x6);
306: }
307: }
308:
309: void st_flush_trace_buffer(void)
310: {
311: flush_trace_file(true);
312: }
313:
314: void trace_print_events(FILE *stream, fprintf_function stream_printf)
315: {
316: unsigned int i;
317:
318: for (i = 0; i < NR_TRACE_EVENTS; i++) {
319: stream_printf(stream, "%s [Event ID %u] : state %u\n",
320: trace_list[i].tp_name, i, trace_list[i].state);
321: }
322: }
323:
324: bool trace_event_set_state(const char *name, bool state)
325: {
326: unsigned int i;
327: unsigned int len;
328: bool wildcard = false;
329: bool matched = false;
330:
331: len = strlen(name);
332: if (len > 0 && name[len - 1] == '*') {
333: wildcard = true;
334: len -= 1;
335: }
336: for (i = 0; i < NR_TRACE_EVENTS; i++) {
337: if (wildcard) {
338: if (!strncmp(trace_list[i].tp_name, name, len)) {
339: trace_list[i].state = state;
340: matched = true;
341: }
342: continue;
343: }
344: if (!strcmp(trace_list[i].tp_name, name)) {
345: trace_list[i].state = state;
346: return true;
347: }
348: }
349: return matched;
350: }
351:
352: /* Helper function to create a thread with signals blocked. Use glib's
353: * portable threads since QEMU abstractions cannot be used due to reentrancy in
354: * the tracer. Also note the signal masking on POSIX hosts so that the thread
355: * does not steal signals when the rest of the program wants them blocked.
356: */
357: static GThread *trace_thread_create(GThreadFunc fn)
358: {
359: GThread *thread;
360: #ifndef _WIN32
361: sigset_t set, oldset;
362:
363: sigfillset(&set);
364: pthread_sigmask(SIG_SETMASK, &set, &oldset);
365: #endif
366: thread = g_thread_create(writeout_thread, NULL, FALSE, NULL);
367: #ifndef _WIN32
368: pthread_sigmask(SIG_SETMASK, &oldset, NULL);
369: #endif
370:
371: return thread;
372: }
373:
374: bool trace_backend_init(const char *events, const char *file)
375: {
376: GThread *thread;
377:
378: if (!g_thread_supported()) {
379: g_thread_init(NULL);
380: }
381:
382: trace_available_cond = g_cond_new();
383: trace_empty_cond = g_cond_new();
384:
385: thread = trace_thread_create(writeout_thread);
386: if (!thread) {
387: fprintf(stderr, "warning: unable to initialize simple trace backend\n");
388: return false;
389: }
390:
391: atexit(st_flush_trace_buffer);
392: trace_backend_init_events(events);
393: st_set_trace_file(file);
394: return true;
395: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.