1 /* -----------------------------------------------------------------------------
3 * (c) The GHC Team, 2008-2009
5 * Support for fast binary event logging.
7 * ---------------------------------------------------------------------------*/
9 #include "PosixSource.h"
15 #include "Capability.h"
24 static char *event_log_filename = NULL;
26 // File for logging events
27 FILE *event_log_file = NULL;
29 #define EVENT_LOG_SIZE 2 * (1024 * 1024) // 2MB
31 static int flushCount;
33 // Struct for record keeping of buffer to store event types and events.
34 typedef struct _EventsBuf {
41 EventsBuf *capEventBuf; // one EventsBuf for each Capability
43 EventsBuf eventBuf; // an EventsBuf not associated with any Capability
45 Mutex eventBufMutex; // protected by this mutex
49 [EVENT_CREATE_THREAD] = "Create thread",
50 [EVENT_RUN_THREAD] = "Run thread",
51 [EVENT_STOP_THREAD] = "Stop thread",
52 [EVENT_THREAD_RUNNABLE] = "Thread runnable",
53 [EVENT_MIGRATE_THREAD] = "Migrate thread",
54 [EVENT_RUN_SPARK] = "Run spark",
55 [EVENT_STEAL_SPARK] = "Steal spark",
56 [EVENT_SHUTDOWN] = "Shutdown",
57 [EVENT_THREAD_WAKEUP] = "Wakeup thread",
58 [EVENT_GC_START] = "Starting GC",
59 [EVENT_GC_END] = "Finished GC",
60 [EVENT_REQUEST_SEQ_GC] = "Request sequential GC",
61 [EVENT_REQUEST_PAR_GC] = "Request parallel GC",
62 [EVENT_CREATE_SPARK_THREAD] = "Create spark thread",
63 [EVENT_LOG_CAP_MSG] = "Log Capability message",
64 [EVENT_LOG_MSG] = "Log message",
65 [EVENT_STARTUP] = "Startup",
66 [EVENT_BLOCK_MARKER] = "Block marker"
71 typedef struct _EventType {
72 EventTypeNum etNum; // Event Type number.
73 nat size; // size of the payload in bytes
74 char *desc; // Description
77 EventType eventTypes[NUM_EVENT_TAGS];
79 static void initEventsBuf(EventsBuf* eb, StgWord64 size);
80 static void resetEventsBuf(EventsBuf* eb);
81 static void printAndClearEventBuf (EventsBuf *eventsBuf);
83 static void postEventType(EventsBuf *eb, EventType *et);
85 static void postBlockMarker(EventsBuf *eb);
86 static void closeBlockMarker(EventsBuf *ebuf);
88 static StgBool hasRoomForEvent(EventsBuf *eb, EventTypeNum eNum);
89 static StgBool hasRoomForVariableEvent(EventsBuf *eb, nat payload_bytes);
91 static inline void postWord8(EventsBuf *eb, StgWord8 i)
96 static inline void postWord16(EventsBuf *eb, StgWord16 i)
98 postWord8(eb, (StgWord8)(i >> 8));
99 postWord8(eb, (StgWord8)i);
102 static inline void postWord32(EventsBuf *eb, StgWord32 i)
104 postWord16(eb, (StgWord16)(i >> 16));
105 postWord16(eb, (StgWord16)i);
108 static inline void postWord64(EventsBuf *eb, StgWord64 i)
110 postWord32(eb, (StgWord32)(i >> 32));
111 postWord32(eb, (StgWord32)i);
114 static inline void postBuf(EventsBuf *eb, StgWord8 *buf, nat size)
116 memcpy(eb->pos, buf, size);
120 static inline StgWord64 time_ns(void)
121 { return stat_getElapsedTime() * (1000000000LL/TICKS_PER_SECOND); }
123 static inline void postEventTypeNum(EventsBuf *eb, EventTypeNum etNum)
124 { postWord16(eb, etNum); }
126 static inline void postTimestamp(EventsBuf *eb)
127 { postWord64(eb, time_ns()); }
129 static inline void postThreadID(EventsBuf *eb, EventThreadID id)
130 { postWord32(eb,id); }
132 static inline void postCapNo(EventsBuf *eb, EventCapNo no)
133 { postWord16(eb,no); }
135 static inline void postPayloadSize(EventsBuf *eb, EventPayloadSize size)
136 { postWord16(eb,size); }
138 static inline void postEventHeader(EventsBuf *eb, EventTypeNum type)
140 postEventTypeNum(eb, type);
144 static inline void postInt8(EventsBuf *eb, StgInt8 i)
145 { postWord8(eb, (StgWord8)i); }
147 static inline void postInt16(EventsBuf *eb, StgInt16 i)
148 { postWord16(eb, (StgWord16)i); }
150 static inline void postInt32(EventsBuf *eb, StgInt32 i)
151 { postWord32(eb, (StgWord32)i); }
153 static inline void postInt64(EventsBuf *eb, StgInt64 i)
154 { postWord64(eb, (StgWord64)i); }
158 initEventLogging(void)
163 event_log_filename = stgMallocBytes(strlen(prog_name) + 10,
166 if (sizeof(EventDesc) / sizeof(char*) != NUM_EVENT_TAGS) {
167 barf("EventDesc array has the wrong number of elements");
170 sprintf(event_log_filename, "%s.eventlog", prog_name);
172 /* Open event log file for writing. */
173 if ((event_log_file = fopen(event_log_filename, "wb")) == NULL) {
174 sysErrorBelch("initEventLoggin: can't open %s", event_log_filename);
175 stg_exit(EXIT_FAILURE);
179 * Allocate buffer(s) to store events.
180 * Create buffer large enough for the header begin marker, all event
181 * types, and header end marker to prevent checking if buffer has room
182 * for each of these steps, and remove the need to flush the buffer to
183 * disk during initialization.
185 * Use a single buffer to store the header with event types, then flush
186 * the buffer so all buffers are empty for writing events.
189 // XXX n_capabilities hasn't been initislised yet
190 n_caps = RtsFlags.ParFlags.nNodes;
194 capEventBuf = stgMallocBytes(n_caps * sizeof(EventsBuf),"initEventLogging");
196 for (c = 0; c < n_caps; ++c) {
197 // Init buffer for events.
198 initEventsBuf(&capEventBuf[c], EVENT_LOG_SIZE);
200 initEventsBuf(&eventBuf, EVENT_LOG_SIZE);
202 // Write in buffer: the header begin marker.
203 postInt32(&eventBuf, EVENT_HEADER_BEGIN);
205 // Mark beginning of event types in the header.
206 postInt32(&eventBuf, EVENT_HET_BEGIN);
207 for (t = 0; t < NUM_EVENT_TAGS; ++t) {
209 eventTypes[t].etNum = t;
210 eventTypes[t].desc = EventDesc[t];
213 case EVENT_CREATE_THREAD: // (cap, thread)
214 case EVENT_RUN_THREAD: // (cap, thread)
215 case EVENT_THREAD_RUNNABLE: // (cap, thread)
216 case EVENT_RUN_SPARK: // (cap, thread)
217 case EVENT_CREATE_SPARK_THREAD: // (cap, spark_thread)
218 eventTypes[t].size = sizeof(EventCapNo) + sizeof(EventThreadID);
221 case EVENT_MIGRATE_THREAD: // (cap, thread, new_cap)
222 case EVENT_STEAL_SPARK: // (cap, thread, victim_cap)
223 case EVENT_THREAD_WAKEUP: // (cap, thread, other_cap)
225 sizeof(EventCapNo) + sizeof(EventThreadID) + sizeof(EventCapNo);
228 case EVENT_STOP_THREAD: // (cap, thread, status)
230 sizeof(EventCapNo) + sizeof(EventThreadID) + sizeof(StgWord16);
233 case EVENT_SHUTDOWN: // (cap)
234 case EVENT_REQUEST_SEQ_GC: // (cap)
235 case EVENT_REQUEST_PAR_GC: // (cap)
236 case EVENT_GC_START: // (cap)
237 case EVENT_GC_END: // (cap)
238 eventTypes[t].size = sizeof(EventCapNo);
241 case EVENT_LOG_MSG: // (msg)
242 case EVENT_LOG_CAP_MSG: // (cap,msg)
243 eventTypes[t].size = 0xffff;
247 eventTypes[t].size = sizeof(EventCapNo);
250 case EVENT_BLOCK_MARKER:
251 eventTypes[t].size = sizeof(StgWord32) + sizeof(EventTimestamp);
255 continue; /* ignore deprecated events */
258 // Write in buffer: the start event type.
259 postEventType(&eventBuf, &eventTypes[t]);
262 // Mark end of event types in the header.
263 postInt32(&eventBuf, EVENT_HET_END);
265 // Write in buffer: the header end marker.
266 postInt32(&eventBuf, EVENT_HEADER_END);
268 // Prepare event buffer for events (data).
269 postInt32(&eventBuf, EVENT_DATA_BEGIN);
271 // Flush capEventBuf with header.
273 * Flush header and data begin marker to the file, thus preparing the
274 * file to have events written to it.
276 printAndClearEventBuf(&eventBuf);
278 for (c = 0; c < n_caps; ++c) {
279 postBlockMarker(&capEventBuf[c]);
284 endEventLogging(void)
288 // Flush all events remaining in the buffers.
289 for (c = 0; c < n_capabilities; ++c) {
290 printAndClearEventBuf(&capEventBuf[c]);
292 printAndClearEventBuf(&eventBuf);
294 // Mark end of events (data).
295 postEventTypeNum(&eventBuf, EVENT_DATA_END);
297 // Flush the end of data marker.
298 printAndClearEventBuf(&eventBuf);
300 if (event_log_file != NULL) {
301 fclose(event_log_file);
306 freeEventLogging(void)
310 // Free events buffer.
311 for (c = 0; c < n_capabilities; ++c) {
312 if (capEventBuf[c].begin != NULL)
313 stgFree(capEventBuf[c].begin);
315 if (capEventBuf != NULL) {
316 stgFree(capEventBuf);
318 if (event_log_filename != NULL) {
319 stgFree(event_log_filename);
324 * Post an event message to the capability's eventlog buffer.
325 * If the buffer is full, prints out the buffer and clears it.
328 postSchedEvent (Capability *cap,
335 eb = &capEventBuf[cap->no];
337 if (!hasRoomForEvent(eb, tag)) {
338 // Flush event buffer to make room for new event.
339 printAndClearEventBuf(eb);
342 postEventHeader(eb, tag);
343 postCapNo(eb, cap->no);
346 case EVENT_CREATE_THREAD: // (cap, thread)
347 case EVENT_RUN_THREAD: // (cap, thread)
348 case EVENT_THREAD_RUNNABLE: // (cap, thread)
349 case EVENT_RUN_SPARK: // (cap, thread)
351 postThreadID(eb,thread);
355 case EVENT_CREATE_SPARK_THREAD: // (cap, spark_thread)
357 postThreadID(eb,other /* spark_thread */);
361 case EVENT_MIGRATE_THREAD: // (cap, thread, new_cap)
362 case EVENT_STEAL_SPARK: // (cap, thread, victim_cap)
363 case EVENT_THREAD_WAKEUP: // (cap, thread, other_cap)
365 postThreadID(eb,thread);
366 postCapNo(eb,other /* new_cap | victim_cap | other_cap */);
370 case EVENT_STOP_THREAD: // (cap, thread, status)
372 postThreadID(eb,thread);
373 postWord16(eb,other /* status */);
377 case EVENT_SHUTDOWN: // (cap)
378 case EVENT_REQUEST_SEQ_GC: // (cap)
379 case EVENT_REQUEST_PAR_GC: // (cap)
380 case EVENT_GC_START: // (cap)
381 case EVENT_GC_END: // (cap)
387 barf("postEvent: unknown event tag %d", tag);
393 void postMsg(char *msg, va_list ap)
399 size = vsnprintf(buf,BUF,msg,ap);
405 ACQUIRE_LOCK(&eventBufMutex);
408 if (!hasRoomForVariableEvent(eb, size)) {
409 // Flush event buffer to make room for new event.
410 printAndClearEventBuf(eb);
413 postEventHeader(eb, EVENT_LOG_MSG);
414 postPayloadSize(eb, size);
415 postBuf(eb,(StgWord8*)buf,size);
417 RELEASE_LOCK(&eventBufMutex);
420 void postCapMsg(Capability *cap, char *msg, va_list ap)
426 size = vsnprintf(buf,BUF,msg,ap);
432 eb = &capEventBuf[cap->no];
434 if (!hasRoomForVariableEvent(eb, size)) {
435 // Flush event buffer to make room for new event.
436 printAndClearEventBuf(eb);
439 postEventHeader(eb, EVENT_LOG_MSG);
440 postPayloadSize(eb, size + sizeof(EventCapNo));
441 postCapNo(eb, cap->no);
442 postBuf(eb,(StgWord8*)buf,size);
445 void closeBlockMarker (EventsBuf *ebuf)
449 // (type:16, time:64, size:32, end_time:64)
450 *(StgWord32 *)(ebuf->marker + 10) = ebuf->pos - ebuf->marker;
451 *(StgWord64 *)(ebuf->marker + 14) = time_ns();
457 void postBlockMarker (EventsBuf *eb)
459 if (!hasRoomForEvent(eb, EVENT_BLOCK_MARKER)) {
460 printAndClearEventBuf(eb);
463 closeBlockMarker(eb);
465 eb->marker = eb->pos;
466 postEventHeader(eb, EVENT_BLOCK_MARKER);
467 postWord32(eb,0); // these get filled in later by closeBlockMarker();
471 void printAndClearEventBuf (EventsBuf *ebuf)
473 StgWord64 numBytes = 0, written = 0;
475 closeBlockMarker(ebuf);
477 if (ebuf->begin != NULL && ebuf->pos != ebuf->begin)
479 numBytes = ebuf->pos - ebuf->begin;
481 written = fwrite(ebuf->begin, 1, numBytes, event_log_file);
482 if (written != numBytes) {
484 "printAndClearEventLog: fwrite() failed, written=%" FMT_Word64
485 " doesn't match numBytes=%" FMT_Word64, written, numBytes);
489 resetEventsBuf(ebuf);
492 postBlockMarker(ebuf);
496 void initEventsBuf(EventsBuf* eb, StgWord64 size)
498 eb->begin = eb->pos = stgMallocBytes(size, "initEventsBuf");
503 void resetEventsBuf(EventsBuf* eb)
508 StgBool hasRoomForEvent(EventsBuf *eb, EventTypeNum eNum)
512 size = sizeof(EventTypeNum) + sizeof(EventTimestamp) + eventTypes[eNum].size;
514 if (eb->pos + size > eb->begin + eb->size) {
515 return 0; // Not enough space.
517 return 1; // Buf has enough space for the event.
521 StgBool hasRoomForVariableEvent(EventsBuf *eb, nat payload_bytes)
525 size = sizeof(EventTypeNum) + sizeof(EventTimestamp) +
526 sizeof(EventPayloadSize) + payload_bytes;
528 if (eb->pos + size > eb->begin + eb->size) {
529 return 0; // Not enough space.
531 return 1; // Buf has enough space for the event.
535 void postEventType(EventsBuf *eb, EventType *et)
540 postInt32(eb, EVENT_ET_BEGIN);
541 postEventTypeNum(eb, et->etNum);
542 postWord16(eb, (StgWord16)et->size);
543 desclen = strlen(et->desc);
544 postWord32(eb, desclen);
545 for (d = 0; d < desclen; ++d) {
546 postInt8(eb, (StgInt8)et->desc[d]);
548 postWord32(eb, 0); // no extensions yet
549 postInt32(eb, EVENT_ET_END);