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 /** Trace file header event ID */
18 #define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
20 /** Trace file magic number */
21 #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
23 /** Trace file version number, bump if format changes */
24 #define HEADER_VERSION 0
26 /** Trace buffer entry */
29 uint64_t timestamp_ns
;
39 TRACE_BUF_LEN
= 64 * 1024 / sizeof(TraceRecord
),
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;
48 void st_print_trace_file_status(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
50 stream_printf(stream
, "Trace file \"%s\" %s.\n",
51 trace_file_name
, trace_file_enabled
? "on" : "off");
54 static bool write_header(FILE *fp
)
56 static const TraceRecord header
= {
57 .event
= HEADER_EVENT_ID
,
58 .timestamp_ns
= HEADER_MAGIC
,
62 return fwrite(&header
, sizeof header
, 1, fp
) == 1;
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.
70 bool st_set_trace_file(const char *file
)
72 st_set_trace_file_enabled(false);
74 free(trace_file_name
);
77 if (asprintf(&trace_file_name
, CONFIG_TRACE_FILE
, getpid()) < 0) {
78 trace_file_name
= NULL
;
82 if (asprintf(&trace_file_name
, "%s", file
) < 0) {
83 trace_file_name
= NULL
;
88 st_set_trace_file_enabled(true);
92 static void flush_trace_file(void)
94 /* If the trace file is not open yet, open it now */
96 trace_fp
= fopen(trace_file_name
, "w");
98 /* Avoid repeatedly trying to open file on failure */
99 trace_file_enabled
= false;
102 write_header(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
);
111 void st_flush_trace_buffer(void)
113 if (trace_file_enabled
) {
117 /* Discard written trace records */
121 void st_set_trace_file_enabled(bool enable
)
123 if (enable
== trace_file_enabled
) {
124 return; /* no change */
127 /* Flush/discard trace buffer */
128 st_flush_trace_buffer();
130 /* To disable, close trace file */
136 trace_file_enabled
= enable
;
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
)
142 TraceRecord
*rec
= &trace_buf
[trace_idx
];
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.
149 clock_gettime(CLOCK_MONOTONIC
, &ts
);
151 if (!trace_list
[event
].state
) {
156 rec
->timestamp_ns
= ts
.tv_sec
* 1000000000LL + ts
.tv_nsec
;
164 if (++trace_idx
== TRACE_BUF_LEN
) {
165 st_flush_trace_buffer();
169 void trace0(TraceEventID event
)
171 trace(event
, 0, 0, 0, 0, 0, 0);
174 void trace1(TraceEventID event
, uint64_t x1
)
176 trace(event
, x1
, 0, 0, 0, 0, 0);
179 void trace2(TraceEventID event
, uint64_t x1
, uint64_t x2
)
181 trace(event
, x1
, x2
, 0, 0, 0, 0);
184 void trace3(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
)
186 trace(event
, x1
, x2
, x3
, 0, 0, 0);
189 void trace4(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
)
191 trace(event
, x1
, x2
, x3
, x4
, 0, 0);
194 void trace5(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
, uint64_t x5
)
196 trace(event
, x1
, x2
, x3
, x4
, x5
, 0);
199 void trace6(TraceEventID event
, uint64_t x1
, uint64_t x2
, uint64_t x3
, uint64_t x4
, uint64_t x5
, uint64_t x6
)
201 trace(event
, x1
, x2
, x3
, x4
, x5
, x6
);
205 * Flush the trace buffer on exit
207 static void __attribute__((constructor
)) st_init(void)
209 atexit(st_flush_trace_buffer
);
212 void st_print_trace(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
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
);
223 void st_print_trace_events(FILE *stream
, int (*stream_printf
)(FILE *stream
, const char *fmt
, ...))
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
);
233 static TraceEvent
* find_trace_event_by_name(const char *tname
)
241 for (i
= 0; i
< NR_TRACE_EVENTS
; i
++) {
242 if (!strcmp(trace_list
[i
].tp_name
, tname
)) {
243 return &trace_list
[i
];
246 return NULL
; /* indicates end of list reached without a match */
249 void st_change_trace_event_state(const char *tname
, bool tstate
)
253 tp
= find_trace_event_by_name(tname
);