xref: /openbmc/qemu/trace/simple.c (revision db3bf869)
1edb47ec4SLluís /*
2edb47ec4SLluís  * Simple trace backend
3edb47ec4SLluís  *
4edb47ec4SLluís  * Copyright IBM, Corp. 2010
5edb47ec4SLluís  *
6edb47ec4SLluís  * This work is licensed under the terms of the GNU GPL, version 2.  See
7edb47ec4SLluís  * the COPYING file in the top-level directory.
8edb47ec4SLluís  *
9edb47ec4SLluís  */
10edb47ec4SLluís 
11edb47ec4SLluís #include <stdlib.h>
12edb47ec4SLluís #include <stdint.h>
13edb47ec4SLluís #include <stdio.h>
14edb47ec4SLluís #include <time.h>
1585aff158SStefan Hajnoczi #ifndef _WIN32
16edb47ec4SLluís #include <signal.h>
17edb47ec4SLluís #include <pthread.h>
1885aff158SStefan Hajnoczi #endif
19edb47ec4SLluís #include "qemu-timer.h"
20edb47ec4SLluís #include "trace.h"
21e4858974SLluís #include "trace/control.h"
22edb47ec4SLluís 
23edb47ec4SLluís /** Trace file header event ID */
24edb47ec4SLluís #define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
25edb47ec4SLluís 
26edb47ec4SLluís /** Trace file magic number */
27edb47ec4SLluís #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
28edb47ec4SLluís 
29edb47ec4SLluís /** Trace file version number, bump if format changes */
30edb47ec4SLluís #define HEADER_VERSION 0
31edb47ec4SLluís 
32edb47ec4SLluís /** Records were dropped event ID */
33edb47ec4SLluís #define DROPPED_EVENT_ID (~(uint64_t)0 - 1)
34edb47ec4SLluís 
35edb47ec4SLluís /** Trace record is valid */
36edb47ec4SLluís #define TRACE_RECORD_VALID ((uint64_t)1 << 63)
37edb47ec4SLluís 
38edb47ec4SLluís /** Trace buffer entry */
39edb47ec4SLluís typedef struct {
40edb47ec4SLluís     uint64_t event;
41edb47ec4SLluís     uint64_t timestamp_ns;
42edb47ec4SLluís     uint64_t x1;
43edb47ec4SLluís     uint64_t x2;
44edb47ec4SLluís     uint64_t x3;
45edb47ec4SLluís     uint64_t x4;
46edb47ec4SLluís     uint64_t x5;
47edb47ec4SLluís     uint64_t x6;
48edb47ec4SLluís } TraceRecord;
49edb47ec4SLluís 
50edb47ec4SLluís enum {
51edb47ec4SLluís     TRACE_BUF_LEN = 4096,
52edb47ec4SLluís     TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4,
53edb47ec4SLluís };
54edb47ec4SLluís 
55edb47ec4SLluís /*
56edb47ec4SLluís  * Trace records are written out by a dedicated thread.  The thread waits for
57edb47ec4SLluís  * records to become available, writes them out, and then waits again.
58edb47ec4SLluís  */
5985aff158SStefan Hajnoczi static GStaticMutex trace_lock = G_STATIC_MUTEX_INIT;
6085aff158SStefan Hajnoczi static GCond *trace_available_cond;
6185aff158SStefan Hajnoczi static GCond *trace_empty_cond;
62edb47ec4SLluís static bool trace_available;
63edb47ec4SLluís static bool trace_writeout_enabled;
64edb47ec4SLluís 
65edb47ec4SLluís static TraceRecord trace_buf[TRACE_BUF_LEN];
66edb47ec4SLluís static unsigned int trace_idx;
67edb47ec4SLluís static FILE *trace_fp;
68edb47ec4SLluís static char *trace_file_name = NULL;
69edb47ec4SLluís 
70edb47ec4SLluís /**
71edb47ec4SLluís  * Read a trace record from the trace buffer
72edb47ec4SLluís  *
73edb47ec4SLluís  * @idx         Trace buffer index
74edb47ec4SLluís  * @record      Trace record to fill
75edb47ec4SLluís  *
76edb47ec4SLluís  * Returns false if the record is not valid.
77edb47ec4SLluís  */
78edb47ec4SLluís static bool get_trace_record(unsigned int idx, TraceRecord *record)
79edb47ec4SLluís {
80edb47ec4SLluís     if (!(trace_buf[idx].event & TRACE_RECORD_VALID)) {
81edb47ec4SLluís         return false;
82edb47ec4SLluís     }
83edb47ec4SLluís 
84edb47ec4SLluís     __sync_synchronize(); /* read memory barrier before accessing record */
85edb47ec4SLluís 
86edb47ec4SLluís     *record = trace_buf[idx];
87edb47ec4SLluís     record->event &= ~TRACE_RECORD_VALID;
88edb47ec4SLluís     return true;
89edb47ec4SLluís }
90edb47ec4SLluís 
91edb47ec4SLluís /**
92edb47ec4SLluís  * Kick writeout thread
93edb47ec4SLluís  *
94edb47ec4SLluís  * @wait        Whether to wait for writeout thread to complete
95edb47ec4SLluís  */
96edb47ec4SLluís static void flush_trace_file(bool wait)
97edb47ec4SLluís {
9885aff158SStefan Hajnoczi     g_static_mutex_lock(&trace_lock);
99edb47ec4SLluís     trace_available = true;
10085aff158SStefan Hajnoczi     g_cond_signal(trace_available_cond);
101edb47ec4SLluís 
102edb47ec4SLluís     if (wait) {
10385aff158SStefan Hajnoczi         g_cond_wait(trace_empty_cond, g_static_mutex_get_mutex(&trace_lock));
104edb47ec4SLluís     }
105edb47ec4SLluís 
10685aff158SStefan Hajnoczi     g_static_mutex_unlock(&trace_lock);
107edb47ec4SLluís }
108edb47ec4SLluís 
109edb47ec4SLluís static void wait_for_trace_records_available(void)
110edb47ec4SLluís {
11185aff158SStefan Hajnoczi     g_static_mutex_lock(&trace_lock);
112edb47ec4SLluís     while (!(trace_available && trace_writeout_enabled)) {
11385aff158SStefan Hajnoczi         g_cond_signal(trace_empty_cond);
11485aff158SStefan Hajnoczi         g_cond_wait(trace_available_cond,
11585aff158SStefan Hajnoczi                     g_static_mutex_get_mutex(&trace_lock));
116edb47ec4SLluís     }
117edb47ec4SLluís     trace_available = false;
11885aff158SStefan Hajnoczi     g_static_mutex_unlock(&trace_lock);
119edb47ec4SLluís }
120edb47ec4SLluís 
12185aff158SStefan Hajnoczi static gpointer writeout_thread(gpointer opaque)
122edb47ec4SLluís {
123edb47ec4SLluís     TraceRecord record;
124edb47ec4SLluís     unsigned int writeout_idx = 0;
125edb47ec4SLluís     unsigned int num_available, idx;
126edb47ec4SLluís     size_t unused __attribute__ ((unused));
127edb47ec4SLluís 
128edb47ec4SLluís     for (;;) {
129edb47ec4SLluís         wait_for_trace_records_available();
130edb47ec4SLluís 
131edb47ec4SLluís         num_available = trace_idx - writeout_idx;
132edb47ec4SLluís         if (num_available > TRACE_BUF_LEN) {
133edb47ec4SLluís             record = (TraceRecord){
134edb47ec4SLluís                 .event = DROPPED_EVENT_ID,
135edb47ec4SLluís                 .x1 = num_available,
136edb47ec4SLluís             };
137edb47ec4SLluís             unused = fwrite(&record, sizeof(record), 1, trace_fp);
138edb47ec4SLluís             writeout_idx += num_available;
139edb47ec4SLluís         }
140edb47ec4SLluís 
141edb47ec4SLluís         idx = writeout_idx % TRACE_BUF_LEN;
142edb47ec4SLluís         while (get_trace_record(idx, &record)) {
143edb47ec4SLluís             trace_buf[idx].event = 0; /* clear valid bit */
144edb47ec4SLluís             unused = fwrite(&record, sizeof(record), 1, trace_fp);
145edb47ec4SLluís             idx = ++writeout_idx % TRACE_BUF_LEN;
146edb47ec4SLluís         }
147edb47ec4SLluís 
148edb47ec4SLluís         fflush(trace_fp);
149edb47ec4SLluís     }
150edb47ec4SLluís     return NULL;
151edb47ec4SLluís }
152edb47ec4SLluís 
153edb47ec4SLluís static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
154edb47ec4SLluís                   uint64_t x4, uint64_t x5, uint64_t x6)
155edb47ec4SLluís {
156edb47ec4SLluís     unsigned int idx;
157edb47ec4SLluís     uint64_t timestamp;
158edb47ec4SLluís 
159edb47ec4SLluís     if (!trace_list[event].state) {
160edb47ec4SLluís         return;
161edb47ec4SLluís     }
162edb47ec4SLluís 
163edb47ec4SLluís     timestamp = get_clock();
164edb47ec4SLluís 
16585aff158SStefan Hajnoczi     idx = g_atomic_int_exchange_and_add((gint *)&trace_idx, 1) % TRACE_BUF_LEN;
166edb47ec4SLluís     trace_buf[idx] = (TraceRecord){
167edb47ec4SLluís         .event = event,
168edb47ec4SLluís         .timestamp_ns = timestamp,
169edb47ec4SLluís         .x1 = x1,
170edb47ec4SLluís         .x2 = x2,
171edb47ec4SLluís         .x3 = x3,
172edb47ec4SLluís         .x4 = x4,
173edb47ec4SLluís         .x5 = x5,
174edb47ec4SLluís         .x6 = x6,
175edb47ec4SLluís     };
176edb47ec4SLluís     __sync_synchronize(); /* write barrier before marking as valid */
177edb47ec4SLluís     trace_buf[idx].event |= TRACE_RECORD_VALID;
178edb47ec4SLluís 
179edb47ec4SLluís     if ((idx + 1) % TRACE_BUF_FLUSH_THRESHOLD == 0) {
180edb47ec4SLluís         flush_trace_file(false);
181edb47ec4SLluís     }
182edb47ec4SLluís }
183edb47ec4SLluís 
184edb47ec4SLluís void trace0(TraceEventID event)
185edb47ec4SLluís {
186edb47ec4SLluís     trace(event, 0, 0, 0, 0, 0, 0);
187edb47ec4SLluís }
188edb47ec4SLluís 
189edb47ec4SLluís void trace1(TraceEventID event, uint64_t x1)
190edb47ec4SLluís {
191edb47ec4SLluís     trace(event, x1, 0, 0, 0, 0, 0);
192edb47ec4SLluís }
193edb47ec4SLluís 
194edb47ec4SLluís void trace2(TraceEventID event, uint64_t x1, uint64_t x2)
195edb47ec4SLluís {
196edb47ec4SLluís     trace(event, x1, x2, 0, 0, 0, 0);
197edb47ec4SLluís }
198edb47ec4SLluís 
199edb47ec4SLluís void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3)
200edb47ec4SLluís {
201edb47ec4SLluís     trace(event, x1, x2, x3, 0, 0, 0);
202edb47ec4SLluís }
203edb47ec4SLluís 
204edb47ec4SLluís void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4)
205edb47ec4SLluís {
206edb47ec4SLluís     trace(event, x1, x2, x3, x4, 0, 0);
207edb47ec4SLluís }
208edb47ec4SLluís 
209edb47ec4SLluís void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5)
210edb47ec4SLluís {
211edb47ec4SLluís     trace(event, x1, x2, x3, x4, x5, 0);
212edb47ec4SLluís }
213edb47ec4SLluís 
214edb47ec4SLluís void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6)
215edb47ec4SLluís {
216edb47ec4SLluís     trace(event, x1, x2, x3, x4, x5, x6);
217edb47ec4SLluís }
218edb47ec4SLluís 
219edb47ec4SLluís void st_set_trace_file_enabled(bool enable)
220edb47ec4SLluís {
221edb47ec4SLluís     if (enable == !!trace_fp) {
222edb47ec4SLluís         return; /* no change */
223edb47ec4SLluís     }
224edb47ec4SLluís 
225edb47ec4SLluís     /* Halt trace writeout */
226edb47ec4SLluís     flush_trace_file(true);
227edb47ec4SLluís     trace_writeout_enabled = false;
228edb47ec4SLluís     flush_trace_file(true);
229edb47ec4SLluís 
230edb47ec4SLluís     if (enable) {
231edb47ec4SLluís         static const TraceRecord header = {
232edb47ec4SLluís             .event = HEADER_EVENT_ID,
233edb47ec4SLluís             .timestamp_ns = HEADER_MAGIC,
234edb47ec4SLluís             .x1 = HEADER_VERSION,
235edb47ec4SLluís         };
236edb47ec4SLluís 
2376c2a4074SStefan Hajnoczi         trace_fp = fopen(trace_file_name, "wb");
238edb47ec4SLluís         if (!trace_fp) {
239edb47ec4SLluís             return;
240edb47ec4SLluís         }
241edb47ec4SLluís 
242edb47ec4SLluís         if (fwrite(&header, sizeof header, 1, trace_fp) != 1) {
243edb47ec4SLluís             fclose(trace_fp);
244edb47ec4SLluís             trace_fp = NULL;
245edb47ec4SLluís             return;
246edb47ec4SLluís         }
247edb47ec4SLluís 
248edb47ec4SLluís         /* Resume trace writeout */
249edb47ec4SLluís         trace_writeout_enabled = true;
250edb47ec4SLluís         flush_trace_file(false);
251edb47ec4SLluís     } else {
252edb47ec4SLluís         fclose(trace_fp);
253edb47ec4SLluís         trace_fp = NULL;
254edb47ec4SLluís     }
255edb47ec4SLluís }
256edb47ec4SLluís 
257edb47ec4SLluís /**
258edb47ec4SLluís  * Set the name of a trace file
259edb47ec4SLluís  *
260edb47ec4SLluís  * @file        The trace file name or NULL for the default name-<pid> set at
261edb47ec4SLluís  *              config time
262edb47ec4SLluís  */
263edb47ec4SLluís bool st_set_trace_file(const char *file)
264edb47ec4SLluís {
265edb47ec4SLluís     st_set_trace_file_enabled(false);
266edb47ec4SLluís 
267edb47ec4SLluís     free(trace_file_name);
268edb47ec4SLluís 
269edb47ec4SLluís     if (!file) {
270edb47ec4SLluís         if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) {
271edb47ec4SLluís             trace_file_name = NULL;
272edb47ec4SLluís             return false;
273edb47ec4SLluís         }
274edb47ec4SLluís     } else {
275edb47ec4SLluís         if (asprintf(&trace_file_name, "%s", file) < 0) {
276edb47ec4SLluís             trace_file_name = NULL;
277edb47ec4SLluís             return false;
278edb47ec4SLluís         }
279edb47ec4SLluís     }
280edb47ec4SLluís 
281edb47ec4SLluís     st_set_trace_file_enabled(true);
282edb47ec4SLluís     return true;
283edb47ec4SLluís }
284edb47ec4SLluís 
285edb47ec4SLluís void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
286edb47ec4SLluís {
287edb47ec4SLluís     stream_printf(stream, "Trace file \"%s\" %s.\n",
288edb47ec4SLluís                   trace_file_name, trace_fp ? "on" : "off");
289edb47ec4SLluís }
290edb47ec4SLluís 
291edb47ec4SLluís void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
292edb47ec4SLluís {
293edb47ec4SLluís     unsigned int i;
294edb47ec4SLluís 
295edb47ec4SLluís     for (i = 0; i < TRACE_BUF_LEN; i++) {
296edb47ec4SLluís         TraceRecord record;
297edb47ec4SLluís 
298edb47ec4SLluís         if (!get_trace_record(i, &record)) {
299edb47ec4SLluís             continue;
300edb47ec4SLluís         }
301edb47ec4SLluís         stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64
302edb47ec4SLluís                       " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n",
303edb47ec4SLluís                       record.event, record.x1, record.x2,
304edb47ec4SLluís                       record.x3, record.x4, record.x5,
305edb47ec4SLluís                       record.x6);
306edb47ec4SLluís     }
307edb47ec4SLluís }
308edb47ec4SLluís 
309fc764105SLluís void st_flush_trace_buffer(void)
310fc764105SLluís {
311fc764105SLluís     flush_trace_file(true);
312fc764105SLluís }
313fc764105SLluís 
314fc764105SLluís void trace_print_events(FILE *stream, fprintf_function stream_printf)
315edb47ec4SLluís {
316edb47ec4SLluís     unsigned int i;
317edb47ec4SLluís 
318edb47ec4SLluís     for (i = 0; i < NR_TRACE_EVENTS; i++) {
319edb47ec4SLluís         stream_printf(stream, "%s [Event ID %u] : state %u\n",
320edb47ec4SLluís                       trace_list[i].tp_name, i, trace_list[i].state);
321edb47ec4SLluís     }
322edb47ec4SLluís }
323edb47ec4SLluís 
324fc764105SLluís bool trace_event_set_state(const char *name, bool state)
325edb47ec4SLluís {
326edb47ec4SLluís     unsigned int i;
327454e202dSMark Wu     unsigned int len;
328454e202dSMark Wu     bool wildcard = false;
329454e202dSMark Wu     bool matched = false;
330edb47ec4SLluís 
331454e202dSMark Wu     len = strlen(name);
332454e202dSMark Wu     if (len > 0 && name[len - 1] == '*') {
333454e202dSMark Wu         wildcard = true;
334454e202dSMark Wu         len -= 1;
335454e202dSMark Wu     }
336edb47ec4SLluís     for (i = 0; i < NR_TRACE_EVENTS; i++) {
337454e202dSMark Wu         if (wildcard) {
338454e202dSMark Wu             if (!strncmp(trace_list[i].tp_name, name, len)) {
339454e202dSMark Wu                 trace_list[i].state = state;
340454e202dSMark Wu                 matched = true;
341454e202dSMark Wu             }
342454e202dSMark Wu             continue;
343454e202dSMark Wu         }
344edb47ec4SLluís         if (!strcmp(trace_list[i].tp_name, name)) {
345fc764105SLluís             trace_list[i].state = state;
346edb47ec4SLluís             return true;
347edb47ec4SLluís         }
348edb47ec4SLluís     }
349454e202dSMark Wu     return matched;
350edb47ec4SLluís }
351edb47ec4SLluís 
35285aff158SStefan Hajnoczi /* Helper function to create a thread with signals blocked.  Use glib's
35385aff158SStefan Hajnoczi  * portable threads since QEMU abstractions cannot be used due to reentrancy in
35485aff158SStefan Hajnoczi  * the tracer.  Also note the signal masking on POSIX hosts so that the thread
35585aff158SStefan Hajnoczi  * does not steal signals when the rest of the program wants them blocked.
35685aff158SStefan Hajnoczi  */
35785aff158SStefan Hajnoczi static GThread *trace_thread_create(GThreadFunc fn)
358edb47ec4SLluís {
35985aff158SStefan Hajnoczi     GThread *thread;
36085aff158SStefan Hajnoczi #ifndef _WIN32
361edb47ec4SLluís     sigset_t set, oldset;
362edb47ec4SLluís 
363edb47ec4SLluís     sigfillset(&set);
364edb47ec4SLluís     pthread_sigmask(SIG_SETMASK, &set, &oldset);
36585aff158SStefan Hajnoczi #endif
366*db3bf869SJun Koi     thread = g_thread_create(fn, NULL, FALSE, NULL);
36785aff158SStefan Hajnoczi #ifndef _WIN32
368edb47ec4SLluís     pthread_sigmask(SIG_SETMASK, &oldset, NULL);
36985aff158SStefan Hajnoczi #endif
370edb47ec4SLluís 
37185aff158SStefan Hajnoczi     return thread;
37285aff158SStefan Hajnoczi }
37385aff158SStefan Hajnoczi 
37485aff158SStefan Hajnoczi bool trace_backend_init(const char *events, const char *file)
37585aff158SStefan Hajnoczi {
37685aff158SStefan Hajnoczi     GThread *thread;
37785aff158SStefan Hajnoczi 
37885aff158SStefan Hajnoczi     if (!g_thread_supported()) {
37942ed3727SAlon Levy #if !GLIB_CHECK_VERSION(2, 31, 0)
38085aff158SStefan Hajnoczi         g_thread_init(NULL);
38142ed3727SAlon Levy #else
38242ed3727SAlon Levy         fprintf(stderr, "glib threading failed to initialize.\n");
38342ed3727SAlon Levy         exit(1);
38442ed3727SAlon Levy #endif
38585aff158SStefan Hajnoczi     }
38685aff158SStefan Hajnoczi 
38785aff158SStefan Hajnoczi     trace_available_cond = g_cond_new();
38885aff158SStefan Hajnoczi     trace_empty_cond = g_cond_new();
38985aff158SStefan Hajnoczi 
39085aff158SStefan Hajnoczi     thread = trace_thread_create(writeout_thread);
39185aff158SStefan Hajnoczi     if (!thread) {
392e4858974SLluís         fprintf(stderr, "warning: unable to initialize simple trace backend\n");
39385aff158SStefan Hajnoczi         return false;
39485aff158SStefan Hajnoczi     }
39585aff158SStefan Hajnoczi 
396edb47ec4SLluís     atexit(st_flush_trace_buffer);
39723d15e86SLluís     trace_backend_init_events(events);
398edb47ec4SLluís     st_set_trace_file(file);
399edb47ec4SLluís     return true;
400edb47ec4SLluís }
401