summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralexbiehl <alex.biehl@gmail.com>2017-01-31 16:06:33 -0500
committerBen Gamari <ben@smart-cactus.org>2017-01-31 18:50:21 -0500
commit4dfc6d1c40b298d4b8f136e46420227eda60a03d (patch)
treefce2d978323653ff0ff989fdb72f3585be5b4de4
parent44f079f74869d8cb417e2dcc104517ae7f593e5f (diff)
downloadhaskell-4dfc6d1c40b298d4b8f136e46420227eda60a03d.tar.gz
Abstract over the way eventlogs are flushed
Currently eventlog data is always written to a file `progname.eventlog`. This patch introduces the `flushEventLog` field in `RtsConfig` which allows to customize the writing of eventlog data. One possible scenario is the ongoing live-profile-monitor effort by @NCrashed which slurps all eventlog data through `fluchEventLog`. `flushEventLog` takes a buffer with eventlog data and its size and returns `false` (0) in case eventlog data could not be procesed. Reviewers: simonmar, austin, erikd, bgamari Reviewed By: simonmar, bgamari Subscribers: qnikst, thomie, NCrashed Differential Revision: https://phabricator.haskell.org/D2934
-rw-r--r--docs/users_guide/8.2.1-notes.rst4
-rw-r--r--docs/users_guide/runtime_control.rst43
-rw-r--r--includes/RtsAPI.h4
-rw-r--r--includes/rts/EventLogWriter.h40
-rw-r--r--rts/RtsFlags.c1
-rw-r--r--rts/Trace.c21
-rw-r--r--rts/eventlog/EventLog.c128
-rw-r--r--rts/eventlog/EventLog.h3
-rw-r--r--rts/eventlog/EventLogWriter.c122
9 files changed, 281 insertions, 85 deletions
diff --git a/docs/users_guide/8.2.1-notes.rst b/docs/users_guide/8.2.1-notes.rst
index d934418588..3b04975d87 100644
--- a/docs/users_guide/8.2.1-notes.rst
+++ b/docs/users_guide/8.2.1-notes.rst
@@ -240,6 +240,10 @@ Runtime system
threads to all cores in systems which have multiple processor groups.
(e.g. > 64 cores, see :ghc-ticket:`11054`)
+- Output of :ref:`Event log <rts-eventlog>` data can now be configured.
+ Enabling external tools to collect and analyze the event log data while the
+ application is still running.
+
Build system
~~~~~~~~~~~~
diff --git a/docs/users_guide/runtime_control.rst b/docs/users_guide/runtime_control.rst
index 4bde81a9d8..3c606c92bf 100644
--- a/docs/users_guide/runtime_control.rst
+++ b/docs/users_guide/runtime_control.rst
@@ -177,6 +177,35 @@ e.g., on stack overflow. The hooks for these are as follows:
The message printed if ``malloc`` fails.
+Furthermore GHC lets you specify the way event log data (:rts-flag:`-l`)
+is written through a custom `EventLogWriter`:
+
+``void initEventLogWriter(void)``
+ .. index::
+ single: initEventLogWriter
+
+ Initializes your `EventLogWriter`. This is optional.
+
+``bool writeEventLog(void *eventlog, size_t eventlog_size)``
+ .. index::
+ single: writeEventLog
+
+ Hands buffered event log data to your event log writer.
+ Required for a custom `EventLogWriter`.
+
+ ``void flushEventLog(void)``
+ .. index::
+ single: flushEventLog
+
+ Flush buffers (if any) of your custom `EventLogWriter`. This is
+ optional.
+
+ ``void stopEventLogWriter(void)``
+ .. index::
+ single: stopEventLogWriter
+
+ Called when event logging is about to stop. This is optional.
+
.. _rts-options-misc:
Miscellaneous RTS options
@@ -938,7 +967,7 @@ Tracing
single: eventlog files
When the program is linked with the :ghc-flag:`-eventlog` option
-(:ref:`options-linker`), runtime events can be logged in two ways:
+(:ref:`options-linker`), runtime events can be logged in several ways:
- In binary format to a file for later analysis by a variety of tools.
One such tool is
@@ -946,13 +975,19 @@ When the program is linked with the :ghc-flag:`-eventlog` option
which interprets the event log to produce a visual parallel execution
profile of the program.
+- In binary format to customized event log writer. This enables live
+ analysis of the events while the program is running.
+
- As text to standard output, for debugging purposes.
.. rts-flag:: -l <flags>
- Log events in binary format to the file :file:`{program}.eventlog`.
- Without any ⟨flags⟩ specified, this logs a default set of events,
- suitable for use with tools like ThreadScope.
+ Log events in binary format. Without any ⟨flags⟩ specified, this
+ logs a default set of events, suitable for use with tools like ThreadScope.
+
+ Per default the events are written to :file:`{program}.eventlog` though
+ the mechanism for writing event log data can be overriden with a custom
+ `EventLogWriter`.
For some special use cases you may want more control over which
events are included. The ⟨flags⟩ is a sequence of zero or more
diff --git a/includes/RtsAPI.h b/includes/RtsAPI.h
index bf25c011fc..e5326f759b 100644
--- a/includes/RtsAPI.h
+++ b/includes/RtsAPI.h
@@ -18,6 +18,7 @@ extern "C" {
#include "HsFFI.h"
#include "rts/Time.h"
+#include "rts/EventLogWriter.h"
/*
* Running the scheduler
@@ -79,6 +80,9 @@ typedef struct {
// Whether to retain CAFs (default: false)
HsBool keep_cafs;
+ // Writer a for eventlog.
+ const EventLogWriter *eventlog_writer;
+
// Called before processing command-line flags, so that default
// settings for RtsFlags can be provided.
void (* defaultsHook) (void);
diff --git a/includes/rts/EventLogWriter.h b/includes/rts/EventLogWriter.h
new file mode 100644
index 0000000000..f9cb25fe62
--- /dev/null
+++ b/includes/rts/EventLogWriter.h
@@ -0,0 +1,40 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 2008-2017
+ *
+ * Support for fast binary event logging.
+ *
+ * ---------------------------------------------------------------------------*/
+
+#ifndef EVENTLOG_WRITER_H
+#define EVENTLOG_WRITER_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "Rts.h"
+
+/*
+ * Abstraction for writing eventlog data.
+ */
+typedef struct {
+ // Initialize an EventLogWriter (may be NULL)
+ void (* initEventLogWriter) (void);
+
+ // Write a series of events
+ bool (* writeEventLog) (void *eventlog, size_t eventlog_size);
+
+ // Flush possibly existing buffers (may be NULL)
+ void (* flushEventLog) (void);
+
+ // Close an initialized EventLogOutput (may be NULL)
+ void (* stopEventLogWriter) (void);
+} EventLogWriter;
+
+/*
+ * An EventLogWriter which writes eventlogs to
+ * a file `program.eventlog`.
+ */
+extern const EventLogWriter FileEventLogWriter;
+
+#endif /* EVENTLOG_WRITER_H */
diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c
index c9da13bafc..f924432de4 100644
--- a/rts/RtsFlags.c
+++ b/rts/RtsFlags.c
@@ -64,6 +64,7 @@ const RtsConfig defaultRtsConfig = {
.rts_opts = NULL,
.rts_hs_main = false,
.keep_cafs = false,
+ .eventlog_writer = &FileEventLogWriter,
.defaultsHook = FlagDefaultsHook,
.onExitHook = OnExitHook,
.stackOverflowHook = StackOverflowHook,
diff --git a/rts/Trace.c b/rts/Trace.c
index 91680b7b8a..07717f3ac3 100644
--- a/rts/Trace.c
+++ b/rts/Trace.c
@@ -18,8 +18,10 @@
#include "GetEnv.h"
#include "Stats.h"
#include "eventlog/EventLog.h"
+#include "rts/EventLogWriter.h"
#include "Threads.h"
#include "Printer.h"
+#include "RtsFlags.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -43,8 +45,15 @@ static bool eventlog_enabled;
Starting up / shuttting down the tracing facilities
--------------------------------------------------------------------------- */
+static const EventLogWriter *getEventLogWriter(void)
+{
+ return rtsConfig.eventlog_writer;
+}
+
void initTracing (void)
{
+ const EventLogWriter *eventlog_writer = getEventLogWriter();
+
#ifdef THREADED_RTS
initMutex(&trace_utx);
#endif
@@ -82,14 +91,15 @@ void initTracing (void)
TRACE_spark_full ||
TRACE_user;
- eventlog_enabled = RtsFlags.TraceFlags.tracing == TRACE_EVENTLOG;
+ eventlog_enabled = RtsFlags.TraceFlags.tracing == TRACE_EVENTLOG &&
+ eventlog_writer != NULL;
/* Note: we can have any of the TRACE_* flags turned on even when
eventlog_enabled is off. In the DEBUG way we may be tracing to stderr.
*/
if (eventlog_enabled) {
- initEventLogging();
+ initEventLogging(eventlog_writer);
}
}
@@ -109,9 +119,14 @@ void freeTracing (void)
void resetTracing (void)
{
+ const EventLogWriter *eventlog_writer;
+ eventlog_writer = getEventLogWriter();
+
if (eventlog_enabled) {
abortEventLogging(); // abort eventlog inherited from parent
- initEventLogging(); // child starts its own eventlog
+ if (eventlog_writer != NULL) {
+ initEventLogging(eventlog_writer); // child starts its own eventlog
+ }
}
}
diff --git a/rts/eventlog/EventLog.c b/rts/eventlog/EventLog.c
index 4e4bdb5191..ce4cb3847a 100644
--- a/rts/eventlog/EventLog.c
+++ b/rts/eventlog/EventLog.c
@@ -26,13 +26,7 @@
#include <unistd.h>
#endif
-// PID of the process that writes to event_log_filename (#4512)
-static pid_t event_log_pid = -1;
-
-static char *event_log_filename = NULL;
-
-// File for logging events
-FILE *event_log_file = NULL;
+static const EventLogWriter *event_log_writer;
#define EVENT_LOG_SIZE 2 * (1024 * 1024) // 2MB
@@ -232,55 +226,55 @@ static inline void postInt32(EventsBuf *eb, StgInt32 i)
#define EVENT_SIZE_DYNAMIC (-1)
-void
-initEventLogging(void)
+static void
+initEventLogWriter(void)
{
- StgWord8 t, c;
- uint32_t n_caps;
- char *prog;
-
- prog = stgMallocBytes(strlen(prog_name) + 1, "initEventLogging");
- strcpy(prog, prog_name);
-#ifdef mingw32_HOST_OS
- // on Windows, drop the .exe suffix if there is one
- {
- char *suff;
- suff = strrchr(prog,'.');
- if (suff != NULL && !strcmp(suff,".exe")) {
- *suff = '\0';
- }
+ if (event_log_writer != NULL &&
+ event_log_writer->initEventLogWriter != NULL) {
+ event_log_writer->initEventLogWriter();
}
-#endif
+}
- event_log_filename = stgMallocBytes(strlen(prog)
- + 10 /* .%d */
- + 10 /* .eventlog */,
- "initEventLogging");
+static bool
+writeEventLog(void *eventlog, size_t eventlog_size)
+{
+ if (event_log_writer != NULL &&
+ event_log_writer->writeEventLog != NULL) {
+ return event_log_writer->writeEventLog(eventlog, eventlog_size);
+ } else {
+ return false;
+ }
+}
- if (sizeof(EventDesc) / sizeof(char*) != NUM_GHC_EVENT_TAGS) {
- barf("EventDesc array has the wrong number of elements");
+static void
+stopEventLogWriter(void)
+{
+ if (event_log_writer != NULL &&
+ event_log_writer->stopEventLogWriter != NULL) {
+ event_log_writer->stopEventLogWriter();
}
+}
- if (event_log_pid == -1) { // #4512
- // Single process
- sprintf(event_log_filename, "%s.eventlog", prog);
- event_log_pid = getpid();
- } else {
- // Forked process, eventlog already started by the parent
- // before fork
- event_log_pid = getpid();
- // We don't have a FMT* symbol for pid_t, so we go via Word64
- // to be sure of not losing range. It would be nicer to have a
- // FMT* symbol or similar, though.
- sprintf(event_log_filename, "%s.%" FMT_Word64 ".eventlog",
- prog, (StgWord64)event_log_pid);
+void
+flushEventLog(void)
+{
+ if (event_log_writer != NULL &&
+ event_log_writer->flushEventLog != NULL) {
+ event_log_writer->flushEventLog();
}
- stgFree(prog);
+}
+
+void
+initEventLogging(const EventLogWriter *ev_writer)
+{
+ StgWord8 t, c;
+ uint32_t n_caps;
+
+ event_log_writer = ev_writer;
+ initEventLogWriter();
- /* Open event log file for writing. */
- if ((event_log_file = fopen(event_log_filename, "wb")) == NULL) {
- sysErrorBelch("initEventLogging: can't open %s", event_log_filename);
- stg_exit(EXIT_FAILURE);
+ if (sizeof(EventDesc) / sizeof(char*) != NUM_GHC_EVENT_TAGS) {
+ barf("EventDesc array has the wrong number of elements");
}
/*
@@ -522,9 +516,7 @@ endEventLogging(void)
// Flush the end of data marker.
printAndClearEventBuf(&eventBuf);
- if (event_log_file != NULL) {
- fclose(event_log_file);
- }
+ stopEventLogWriter();
}
void
@@ -568,26 +560,13 @@ freeEventLogging(void)
if (capEventBuf != NULL) {
stgFree(capEventBuf);
}
- if (event_log_filename != NULL) {
- stgFree(event_log_filename);
- }
-}
-
-void
-flushEventLog(void)
-{
- if (event_log_file != NULL) {
- fflush(event_log_file);
- }
}
void
abortEventLogging(void)
{
freeEventLogging();
- if (event_log_file != NULL) {
- fclose(event_log_file);
- }
+ stopEventLogWriter();
}
/*
@@ -1287,18 +1266,13 @@ void printAndClearEventBuf (EventsBuf *ebuf)
if (ebuf->begin != NULL && ebuf->pos != ebuf->begin)
{
- StgInt8 *begin = ebuf->begin;
- while (begin < ebuf->pos) {
- StgWord64 remain = ebuf->pos - begin;
- StgWord64 written = fwrite(begin, 1, remain, event_log_file);
- if (written == 0) {
- debugBelch(
- "printAndClearEventLog: fwrite() failed to write anything;"
- " tried to write numBytes=%" FMT_Word64, remain);
- resetEventsBuf(ebuf);
- return;
- }
- begin += written;
+ size_t elog_size = ebuf->pos - ebuf->begin;
+ if (!writeEventLog(ebuf->begin, elog_size)) {
+ debugBelch(
+ "printAndClearEventLog: could not flush event log"
+ );
+ resetEventsBuf(ebuf);
+ return;
}
resetEventsBuf(ebuf);
diff --git a/rts/eventlog/EventLog.h b/rts/eventlog/EventLog.h
index 462f5f594e..e36c97353e 100644
--- a/rts/eventlog/EventLog.h
+++ b/rts/eventlog/EventLog.h
@@ -10,6 +10,7 @@
#define EVENTLOG_H
#include "rts/EventLogFormat.h"
+#include "rts/EventLogWriter.h"
#include "Capability.h"
#include "BeginPrivate.h"
@@ -21,7 +22,7 @@
*/
extern char *EventTagDesc[];
-void initEventLogging(void);
+void initEventLogging(const EventLogWriter *writer);
void endEventLogging(void);
void freeEventLogging(void);
void abortEventLogging(void); // #4512 - after fork child needs to abort
diff --git a/rts/eventlog/EventLogWriter.c b/rts/eventlog/EventLogWriter.c
new file mode 100644
index 0000000000..d6db743119
--- /dev/null
+++ b/rts/eventlog/EventLogWriter.c
@@ -0,0 +1,122 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team, 2008-2009
+ *
+ * Support for fast binary event logging.
+ *
+ * ---------------------------------------------------------------------------*/
+
+#include "PosixSource.h"
+#include "Rts.h"
+
+#include "RtsUtils.h"
+#include "rts/EventLogWriter.h"
+
+#include <string.h>
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+// PID of the process that writes to event_log_filename (#4512)
+static pid_t event_log_pid = -1;
+
+// File for logging events
+static FILE *event_log_file = NULL;
+
+static void initEventLogFileWriter(void);
+static bool writeEventLogFile(void *eventlog, size_t eventlog_size);
+static void flushEventLogFile(void);
+static void stopEventLogFileWriter(void);
+
+static void
+initEventLogFileWriter(void)
+{
+ char *prog, *event_log_filename;
+
+ prog = stgMallocBytes(strlen(prog_name) + 1, "initEventLogFileWriter");
+ strcpy(prog, prog_name);
+#ifdef mingw32_HOST_OS
+ // on Windows, drop the .exe suffix if there is one
+ {
+ char *suff;
+ suff = strrchr(prog,'.');
+ if (suff != NULL && !strcmp(suff,".exe")) {
+ *suff = '\0';
+ }
+ }
+#endif
+ event_log_filename = stgMallocBytes(strlen(prog)
+ + 10 /* .%d */
+ + 10 /* .eventlog */,
+ "initEventLogFileWriter");
+
+ if (event_log_pid == -1) { // #4512
+ // Single process
+ sprintf(event_log_filename, "%s.eventlog", prog);
+ event_log_pid = getpid();
+ } else {
+ // Forked process, eventlog already started by the parent
+ // before fork
+ event_log_pid = getpid();
+ // We don't have a FMT* symbol for pid_t, so we go via Word64
+ // to be sure of not losing range. It would be nicer to have a
+ // FMT* symbol or similar, though.
+ sprintf(event_log_filename, "%s.%" FMT_Word64 ".eventlog",
+ prog, (StgWord64)event_log_pid);
+ }
+ stgFree(prog);
+
+ /* Open event log file for writing. */
+ if ((event_log_file = fopen(event_log_filename, "wb")) == NULL) {
+ sysErrorBelch(
+ "initEventLogFileWriter: can't open %s", event_log_filename);
+ stg_exit(EXIT_FAILURE);
+ }
+
+ stgFree(event_log_filename);
+}
+
+static bool
+writeEventLogFile(void *eventlog, size_t eventlog_size)
+{
+ unsigned char *begin = eventlog;
+ size_t remain = eventlog_size;
+
+ while (remain > 0) {
+ size_t written = fwrite(begin, 1, remain, event_log_file);
+ if (written == 0) {
+ return false;
+ }
+ remain -= written;
+ begin += written;
+ }
+
+ return true;
+}
+
+static void
+flushEventLogFile(void)
+{
+ if (event_log_file != NULL) {
+ fflush(event_log_file);
+ }
+}
+
+static void
+stopEventLogFileWriter(void)
+{
+ if (event_log_file != NULL) {
+ fclose(event_log_file);
+ }
+}
+
+const EventLogWriter FileEventLogWriter = {
+ .initEventLogWriter = initEventLogFileWriter,
+ .writeEventLog = writeEventLogFile,
+ .flushEventLog = flushEventLogFile,
+ .stopEventLogWriter = stopEventLogFileWriter
+};