Fix crash when using printf format specifiers in traceEvent (#3874)
[ghc-hetmet.git] / rts / eventlog / EventLog.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 2008-2009
4  *
5  * Support for fast binary event logging.
6  *
7  * ---------------------------------------------------------------------------*/
8
9 #include "PosixSource.h"
10 #include "Rts.h"
11
12 #ifdef TRACING
13
14 #include "Trace.h"
15 #include "Capability.h"
16 #include "RtsUtils.h"
17 #include "Stats.h"
18 #include "EventLog.h"
19
20 #include <string.h> 
21 #include <stdio.h>
22
23 static char *event_log_filename = NULL;
24
25 // File for logging events
26 FILE *event_log_file = NULL;
27
28 #define EVENT_LOG_SIZE 2 * (1024 * 1024) // 2MB
29
30 static int flushCount;
31
32 // Struct for record keeping of buffer to store event types and events.
33 typedef struct _EventsBuf {
34   StgInt8 *begin;
35   StgInt8 *pos;
36   StgInt8 *marker;
37   StgWord64 size;
38   EventCapNo capno; // which capability this buffer belongs to, or -1
39 } EventsBuf;
40
41 EventsBuf *capEventBuf; // one EventsBuf for each Capability
42
43 EventsBuf eventBuf; // an EventsBuf not associated with any Capability
44 #ifdef THREADED_RTS
45 Mutex eventBufMutex; // protected by this mutex
46 #endif
47
48 char *EventDesc[] = {
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_MSG]             = "Log message",
64   [EVENT_USER_MSG]            = "User message",
65   [EVENT_STARTUP]             = "Startup",
66   [EVENT_GC_IDLE]             = "GC idle",
67   [EVENT_GC_WORK]             = "GC working",
68   [EVENT_GC_DONE]             = "GC done",
69   [EVENT_BLOCK_MARKER]        = "Block marker"
70 };
71
72 // Event type. 
73
74 typedef struct _EventType {
75   EventTypeNum etNum;  // Event Type number.
76   nat   size;     // size of the payload in bytes
77   char *desc;     // Description
78 } EventType;
79
80 EventType eventTypes[NUM_EVENT_TAGS];
81
82 static void initEventsBuf(EventsBuf* eb, StgWord64 size, EventCapNo capno);
83 static void resetEventsBuf(EventsBuf* eb);
84 static void printAndClearEventBuf (EventsBuf *eventsBuf);
85
86 static void postEventType(EventsBuf *eb, EventType *et);
87
88 static void postLogMsg(EventsBuf *eb, EventTypeNum type, char *msg, va_list ap);
89
90 static void postBlockMarker(EventsBuf *eb);
91 static void closeBlockMarker(EventsBuf *ebuf);
92
93 static StgBool hasRoomForEvent(EventsBuf *eb, EventTypeNum eNum);
94 static StgBool hasRoomForVariableEvent(EventsBuf *eb, nat payload_bytes);
95
96 static inline void postWord8(EventsBuf *eb, StgWord8 i)
97 {
98     *(eb->pos++) = i; 
99 }
100
101 static inline void postWord16(EventsBuf *eb, StgWord16 i)
102 {
103     postWord8(eb, (StgWord8)(i >> 8));
104     postWord8(eb, (StgWord8)i);
105 }
106
107 static inline void postWord32(EventsBuf *eb, StgWord32 i)
108 {
109     postWord16(eb, (StgWord16)(i >> 16));
110     postWord16(eb, (StgWord16)i);
111 }
112
113 static inline void postWord64(EventsBuf *eb, StgWord64 i)
114 {
115     postWord32(eb, (StgWord32)(i >> 32));
116     postWord32(eb, (StgWord32)i);
117 }
118
119 static inline void postBuf(EventsBuf *eb, StgWord8 *buf, nat size)
120 {
121     memcpy(eb->pos, buf, size);
122     eb->pos += size;
123 }
124
125 static inline StgWord64 time_ns(void)
126 { return stat_getElapsedTime() * (1000000000LL/TICKS_PER_SECOND); }
127
128 static inline void postEventTypeNum(EventsBuf *eb, EventTypeNum etNum)
129 { postWord16(eb, etNum); }
130
131 static inline void postTimestamp(EventsBuf *eb)
132 { postWord64(eb, time_ns()); }
133
134 static inline void postThreadID(EventsBuf *eb, EventThreadID id)
135 { postWord32(eb,id); }
136
137 static inline void postCapNo(EventsBuf *eb, EventCapNo no)
138 { postWord16(eb,no); }
139
140 static inline void postPayloadSize(EventsBuf *eb, EventPayloadSize size)
141 { postWord16(eb,size); }
142
143 static inline void postEventHeader(EventsBuf *eb, EventTypeNum type)
144 {
145     postEventTypeNum(eb, type);
146     postTimestamp(eb);
147 }    
148
149 static inline void postInt8(EventsBuf *eb, StgInt8 i)
150 { postWord8(eb, (StgWord8)i); }
151
152 static inline void postInt16(EventsBuf *eb, StgInt16 i)
153 { postWord16(eb, (StgWord16)i); }
154
155 static inline void postInt32(EventsBuf *eb, StgInt32 i)
156 { postWord32(eb, (StgWord32)i); }
157
158 static inline void postInt64(EventsBuf *eb, StgInt64 i)
159 { postWord64(eb, (StgWord64)i); }
160
161
162 void
163 initEventLogging(void)
164 {
165     StgWord8 t, c;
166     nat n_caps;
167
168     event_log_filename = stgMallocBytes(strlen(prog_name) + 10,
169                                         "initEventLogging");
170
171     if (sizeof(EventDesc) / sizeof(char*) != NUM_EVENT_TAGS) {
172         barf("EventDesc array has the wrong number of elements");
173     }
174   
175     sprintf(event_log_filename, "%s.eventlog", prog_name);
176     
177     /* Open event log file for writing. */
178     if ((event_log_file = fopen(event_log_filename, "wb")) == NULL) {
179         sysErrorBelch("initEventLogging: can't open %s", event_log_filename);
180         stg_exit(EXIT_FAILURE);    
181     }
182
183     /* 
184      * Allocate buffer(s) to store events.
185      * Create buffer large enough for the header begin marker, all event
186      * types, and header end marker to prevent checking if buffer has room
187      * for each of these steps, and remove the need to flush the buffer to
188      * disk during initialization.
189      *
190      * Use a single buffer to store the header with event types, then flush 
191      * the buffer so all buffers are empty for writing events.
192      */
193 #ifdef THREADED_RTS
194     // XXX n_capabilities hasn't been initislised yet
195     n_caps = RtsFlags.ParFlags.nNodes;
196 #else
197     n_caps = 1;
198 #endif
199     capEventBuf = stgMallocBytes(n_caps * sizeof(EventsBuf),"initEventLogging");
200
201     for (c = 0; c < n_caps; ++c) {
202         // Init buffer for events.
203         initEventsBuf(&capEventBuf[c], EVENT_LOG_SIZE, c);
204     }
205     initEventsBuf(&eventBuf, EVENT_LOG_SIZE, (EventCapNo)(-1));
206
207     // Write in buffer: the header begin marker.
208     postInt32(&eventBuf, EVENT_HEADER_BEGIN);
209
210     // Mark beginning of event types in the header.
211     postInt32(&eventBuf, EVENT_HET_BEGIN);
212     for (t = 0; t < NUM_EVENT_TAGS; ++t) {
213
214         eventTypes[t].etNum = t;
215         eventTypes[t].desc = EventDesc[t];
216
217         switch (t) {
218         case EVENT_CREATE_THREAD:   // (cap, thread)
219         case EVENT_RUN_THREAD:      // (cap, thread)
220         case EVENT_THREAD_RUNNABLE: // (cap, thread)
221         case EVENT_RUN_SPARK:       // (cap, thread)
222         case EVENT_CREATE_SPARK_THREAD: // (cap, spark_thread)
223             eventTypes[t].size = sizeof(EventThreadID);
224             break;
225
226         case EVENT_MIGRATE_THREAD:  // (cap, thread, new_cap)
227         case EVENT_STEAL_SPARK:     // (cap, thread, victim_cap)
228         case EVENT_THREAD_WAKEUP:   // (cap, thread, other_cap)
229             eventTypes[t].size =
230                 sizeof(EventThreadID) + sizeof(EventCapNo);
231             break;
232
233         case EVENT_STOP_THREAD:     // (cap, thread, status)
234             eventTypes[t].size =
235                 sizeof(EventThreadID) + sizeof(StgWord16);
236             break;
237
238         case EVENT_SHUTDOWN:        // (cap)
239         case EVENT_REQUEST_SEQ_GC:  // (cap)
240         case EVENT_REQUEST_PAR_GC:  // (cap)
241         case EVENT_GC_START:        // (cap)
242         case EVENT_GC_END:          // (cap)
243         case EVENT_STARTUP:
244         case EVENT_GC_IDLE:
245         case EVENT_GC_WORK:
246         case EVENT_GC_DONE:
247             eventTypes[t].size = 0;
248             break;
249
250         case EVENT_LOG_MSG:          // (msg)
251         case EVENT_USER_MSG:         // (msg)
252             eventTypes[t].size = 0xffff;
253             break;
254
255         case EVENT_BLOCK_MARKER:
256             eventTypes[t].size = sizeof(StgWord32) + sizeof(EventTimestamp) + 
257                 sizeof(EventCapNo);
258             break;
259
260         default:
261             continue; /* ignore deprecated events */
262         }
263
264         // Write in buffer: the start event type.
265         postEventType(&eventBuf, &eventTypes[t]);
266     }
267
268     // Mark end of event types in the header.
269     postInt32(&eventBuf, EVENT_HET_END);
270     
271     // Write in buffer: the header end marker.
272     postInt32(&eventBuf, EVENT_HEADER_END);
273     
274     // Prepare event buffer for events (data).
275     postInt32(&eventBuf, EVENT_DATA_BEGIN);
276     
277     // Post a STARTUP event with the number of capabilities
278     postEventHeader(&eventBuf, EVENT_STARTUP);
279     postCapNo(&eventBuf, n_caps);
280
281     // Flush capEventBuf with header.
282     /*
283      * Flush header and data begin marker to the file, thus preparing the
284      * file to have events written to it.
285      */
286     printAndClearEventBuf(&eventBuf);
287
288     for (c = 0; c < n_caps; ++c) {
289         postBlockMarker(&capEventBuf[c]);
290     }
291 }
292
293 void
294 endEventLogging(void)
295 {
296     nat c;
297
298     // Flush all events remaining in the buffers.
299     for (c = 0; c < n_capabilities; ++c) {
300         printAndClearEventBuf(&capEventBuf[c]);
301     }
302     printAndClearEventBuf(&eventBuf);
303     resetEventsBuf(&eventBuf); // we don't want the block marker
304
305     // Mark end of events (data).
306     postEventTypeNum(&eventBuf, EVENT_DATA_END);
307
308     // Flush the end of data marker.
309     printAndClearEventBuf(&eventBuf);
310
311     if (event_log_file != NULL) {
312         fclose(event_log_file);
313     }
314 }
315
316 void 
317 freeEventLogging(void)
318 {
319     StgWord8 c;
320     
321     // Free events buffer.
322     for (c = 0; c < n_capabilities; ++c) {
323         if (capEventBuf[c].begin != NULL) 
324             stgFree(capEventBuf[c].begin);
325     }
326     if (capEventBuf != NULL)  {
327         stgFree(capEventBuf);
328     }
329     if (event_log_filename != NULL) {
330         stgFree(event_log_filename);
331     }
332 }
333
334 /*
335  * Post an event message to the capability's eventlog buffer.
336  * If the buffer is full, prints out the buffer and clears it.
337  */
338 void
339 postSchedEvent (Capability *cap, 
340                 EventTypeNum tag, 
341                 StgThreadID thread, 
342                 StgWord64 other)
343 {
344     EventsBuf *eb;
345
346     eb = &capEventBuf[cap->no];
347
348     if (!hasRoomForEvent(eb, tag)) {
349         // Flush event buffer to make room for new event.
350         printAndClearEventBuf(eb);
351     }
352     
353     postEventHeader(eb, tag);
354
355     switch (tag) {
356     case EVENT_CREATE_THREAD:   // (cap, thread)
357     case EVENT_RUN_THREAD:      // (cap, thread)
358     case EVENT_THREAD_RUNNABLE: // (cap, thread)
359     case EVENT_RUN_SPARK:       // (cap, thread)
360     {
361         postThreadID(eb,thread);
362         break;
363     }
364
365     case EVENT_CREATE_SPARK_THREAD: // (cap, spark_thread)
366     {
367         postThreadID(eb,other /* spark_thread */);
368         break;
369     }
370
371     case EVENT_MIGRATE_THREAD:  // (cap, thread, new_cap)
372     case EVENT_STEAL_SPARK:     // (cap, thread, victim_cap)
373     case EVENT_THREAD_WAKEUP:   // (cap, thread, other_cap)
374     {
375         postThreadID(eb,thread);
376         postCapNo(eb,other /* new_cap | victim_cap | other_cap */);
377         break;
378    }
379
380     case EVENT_STOP_THREAD:     // (cap, thread, status)
381     {
382         postThreadID(eb,thread);
383         postWord16(eb,other /* status */);
384         break;
385     }
386
387     case EVENT_SHUTDOWN:        // (cap)
388     case EVENT_REQUEST_SEQ_GC:  // (cap)
389     case EVENT_REQUEST_PAR_GC:  // (cap)
390     case EVENT_GC_START:        // (cap)
391     case EVENT_GC_END:          // (cap)
392     {
393         break;
394     }
395
396     default:
397         barf("postEvent: unknown event tag %d", tag);
398     }
399 }
400
401 void
402 postEvent (Capability *cap, EventTypeNum tag)
403 {
404     EventsBuf *eb;
405
406     eb = &capEventBuf[cap->no];
407
408     if (!hasRoomForEvent(eb, tag)) {
409         // Flush event buffer to make room for new event.
410         printAndClearEventBuf(eb);
411     }
412
413     postEventHeader(eb, tag);
414 }
415
416 #define BUF 512
417
418 void postLogMsg(EventsBuf *eb, EventTypeNum type, char *msg, va_list ap)
419 {
420     char buf[BUF];
421     nat size;
422
423     size = vsnprintf(buf,BUF,msg,ap);
424     if (size > BUF) {
425         buf[BUF-1] = '\0';
426         size = BUF;
427     }
428
429     if (!hasRoomForVariableEvent(eb, size)) {
430         // Flush event buffer to make room for new event.
431         printAndClearEventBuf(eb);
432     }
433
434     postEventHeader(eb, type);
435     postPayloadSize(eb, size);
436     postBuf(eb,(StgWord8*)buf,size);
437 }
438
439 void postMsg(char *msg, va_list ap)
440 {
441     ACQUIRE_LOCK(&eventBufMutex);
442     postLogMsg(&eventBuf, EVENT_LOG_MSG, msg, ap);
443     RELEASE_LOCK(&eventBufMutex);
444 }
445
446 void postCapMsg(Capability *cap, char *msg, va_list ap)
447 {
448     postLogMsg(&capEventBuf[cap->no], EVENT_LOG_MSG, msg, ap);
449 }
450
451 void postUserMsg(Capability *cap, char *msg, va_list ap)
452 {
453     postLogMsg(&capEventBuf[cap->no], EVENT_USER_MSG, msg, ap);
454 }    
455
456 void closeBlockMarker (EventsBuf *ebuf)
457 {
458     StgInt8* save_pos;
459
460     if (ebuf->marker)
461     {
462         // (type:16, time:64, size:32, end_time:64)
463
464         save_pos = ebuf->pos;
465         ebuf->pos = ebuf->marker + sizeof(EventTypeNum) +
466                     sizeof(EventTimestamp);
467         postWord32(ebuf, save_pos - ebuf->marker);
468         postTimestamp(ebuf);
469         ebuf->pos = save_pos;
470         ebuf->marker = NULL;
471     }
472 }
473
474
475 void postBlockMarker (EventsBuf *eb)
476 {
477     if (!hasRoomForEvent(eb, EVENT_BLOCK_MARKER)) {
478         printAndClearEventBuf(eb);
479     }
480
481     closeBlockMarker(eb);
482
483     eb->marker = eb->pos;
484     postEventHeader(eb, EVENT_BLOCK_MARKER);
485     postWord32(eb,0); // these get filled in later by closeBlockMarker();
486     postWord64(eb,0);
487     postCapNo(eb, eb->capno);
488 }
489
490 void printAndClearEventBuf (EventsBuf *ebuf)
491 {
492     StgWord64 numBytes = 0, written = 0;
493
494     closeBlockMarker(ebuf);
495
496     if (ebuf->begin != NULL && ebuf->pos != ebuf->begin)
497     {
498         numBytes = ebuf->pos - ebuf->begin;
499         
500         written = fwrite(ebuf->begin, 1, numBytes, event_log_file);
501         if (written != numBytes) {
502             debugBelch(
503                 "printAndClearEventLog: fwrite() failed, written=%" FMT_Word64
504                 " doesn't match numBytes=%" FMT_Word64, written, numBytes);
505             return;
506         }
507         
508         resetEventsBuf(ebuf);
509         flushCount++;
510
511         postBlockMarker(ebuf);
512     }
513 }
514
515 void initEventsBuf(EventsBuf* eb, StgWord64 size, EventCapNo capno)
516 {
517     eb->begin = eb->pos = stgMallocBytes(size, "initEventsBuf");
518     eb->size = size;
519     eb->marker = NULL;
520     eb->capno = capno;
521 }
522
523 void resetEventsBuf(EventsBuf* eb)
524 {
525     eb->pos = eb->begin;
526     eb->marker = NULL;
527 }
528
529 StgBool hasRoomForEvent(EventsBuf *eb, EventTypeNum eNum)
530 {
531   nat size;
532
533   size = sizeof(EventTypeNum) + sizeof(EventTimestamp) + eventTypes[eNum].size;
534
535   if (eb->pos + size > eb->begin + eb->size) {
536       return 0; // Not enough space.
537   } else  {
538       return 1; // Buf has enough space for the event.
539   }
540 }
541
542 StgBool hasRoomForVariableEvent(EventsBuf *eb, nat payload_bytes)
543 {
544   nat size;
545
546   size = sizeof(EventTypeNum) + sizeof(EventTimestamp) +
547       sizeof(EventPayloadSize) + payload_bytes;
548
549   if (eb->pos + size > eb->begin + eb->size) {
550       return 0; // Not enough space.
551   } else  {
552       return 1; // Buf has enough space for the event.
553   }
554 }    
555
556 void postEventType(EventsBuf *eb, EventType *et)
557 {
558     StgWord8 d;
559     nat desclen;
560
561     postInt32(eb, EVENT_ET_BEGIN);
562     postEventTypeNum(eb, et->etNum);
563     postWord16(eb, (StgWord16)et->size);
564     desclen = strlen(et->desc);
565     postWord32(eb, desclen);
566     for (d = 0; d < desclen; ++d) {
567         postInt8(eb, (StgInt8)et->desc[d]);
568     }
569     postWord32(eb, 0); // no extensions yet
570     postInt32(eb, EVENT_ET_END);
571 }
572
573 #endif /* TRACING */