Emit various bits of OS process info into the eventlog
authorDuncan Coutts <duncan@well-typed.com>
Thu, 26 May 2011 17:44:41 +0000 (18:44 +0100)
committerDuncan Coutts <duncan@well-typed.com>
Thu, 26 May 2011 17:47:38 +0000 (18:47 +0100)
The process ID, parent process ID, rts name and version
The program arguments and environment.

includes/rts/EventLogFormat.h
rts/GetEnv.h [new file with mode: 0644]
rts/RtsStartup.c
rts/Trace.c
rts/Trace.h
rts/eventlog/EventLog.c
rts/eventlog/EventLog.h
rts/ghc.mk
rts/posix/GetEnv.c [new file with mode: 0644]
rts/win32/GetEnv.c [new file with mode: 0644]

index f3f56c9..1bbb2f0 100644 (file)
 #define EVENT_GC_END              10 /* ()                     */
 #define EVENT_REQUEST_SEQ_GC      11 /* ()                     */
 #define EVENT_REQUEST_PAR_GC      12 /* ()                     */
+/* 13, 14 deprecated */
 #define EVENT_CREATE_SPARK_THREAD 15 /* (spark_thread)         */
 #define EVENT_LOG_MSG             16 /* (message ...)          */
 #define EVENT_STARTUP             17 /* (num_capabilities)     */
 #define EVENT_GC_IDLE             20 /* () */
 #define EVENT_GC_WORK             21 /* () */
 #define EVENT_GC_DONE             22 /* () */
+/* 23, 24 used by eden */
+#define EVENT_CAPSET_CREATE       25 /* (capset, capset_type)  */
+#define EVENT_CAPSET_DELETE       26 /* (capset)               */
+#define EVENT_CAPSET_ASSIGN_CAP   27 /* (capset, cap)          */
+#define EVENT_CAPSET_REMOVE_CAP   28 /* (capset, cap)          */
+/* the RTS identifier is in the form of "GHC-version rts_way"  */
+#define EVENT_RTS_IDENTIFIER      29 /* (capset, name_version_string) */
+/* the vectors in these events are null separated strings             */
+#define EVENT_PROGRAM_ARGS        30 /* (capset, commandline_vector)  */
+#define EVENT_PROGRAM_ENV         31 /* (capset, environment_vector)  */
+#define EVENT_OSPROCESS_PID       32 /* (capset, pid)          */
+#define EVENT_OSPROCESS_PPID      33 /* (capset, parent_pid)   */
 
-#define NUM_EVENT_TAGS            23
+
+/* Range 34 - 59 is available for new events */
+
+/* Range 60 - 80 is used by eden for parallel tracing
+ * see http://www.mathematik.uni-marburg.de/~eden/
+ */
+
+/*
+ * The highest event code +1 that ghc itself emits. Note that some event
+ * ranges higher than this are reserved but not currently emitted by ghc.
+ * This must match the size of the EventDesc[] array in EventLog.c
+ */
+#define NUM_EVENT_TAGS            34
 
 #if 0  /* DEPRECATED EVENTS: */
+/* ghc changed how it handles sparks so these are no longer applicable */
 #define EVENT_CREATE_SPARK        13 /* (cap, thread) */
 #define EVENT_SPARK_TO_THREAD     14 /* (cap, thread, spark_thread) */
+/* these are used by eden but are replaced by new alternatives for ghc */
+#define EVENT_VERSION             23 /* (version_string) */
+#define EVENT_PROGRAM_INVOCATION  24 /* (commandline_string) */
 #endif
 
 /*
  */
 #define THREAD_SUSPENDED_FOREIGN_CALL 6
 
+/*
+ * Capset type values for EVENT_CAPSET_CREATE
+ */
+#define CAPSET_TYPE_CUSTOM      1  /* reserved for end-user applications */
+#define CAPSET_TYPE_OSPROCESS   2  /* caps belong to the same OS process */
+#define CAPSET_TYPE_CLOCKDOMAIN 3  /* caps share a local clock/time      */
+
 #ifndef EVENTLOG_CONSTANTS_ONLY
 
 typedef StgWord16 EventTypeNum;
@@ -160,6 +196,8 @@ typedef StgWord32 EventThreadID;
 typedef StgWord16 EventCapNo;
 typedef StgWord16 EventPayloadSize; /* variable-size events */
 typedef StgWord16 EventThreadStatus; /* status for EVENT_STOP_THREAD */
+typedef StgWord32 EventCapsetID;
+typedef StgWord16 EventCapsetType;   /* types for EVENT_CAPSET_CREATE */
 
 #endif
 
diff --git a/rts/GetEnv.h b/rts/GetEnv.h
new file mode 100644 (file)
index 0000000..5e3d0cf
--- /dev/null
@@ -0,0 +1,23 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team 2011
+ *
+ * OS-independent interface to the process environment variables
+ *
+ * ---------------------------------------------------------------------------*/
+
+#ifndef GETENV_H
+#define GETENV_H
+
+#include "BeginPrivate.h"
+
+/* Get the process environment vector (same style interface as argc/argv)
+ */
+void getProgEnvv  (int *out_envc, char **out_envv[]);
+void freeProgEnvv (int envc, char *envv[]);
+
+/* calls to getProgEnvv must have a corresponding freeProgEnvv */
+
+#include "EndPrivate.h"
+
+#endif /* GETENV_H */
index 0dacd61..c115701 100644 (file)
@@ -153,6 +153,9 @@ hs_init(int *argc, char **argv[])
      */
     initScheduler();
 
+    /* Trace some basic information about the process */
+    traceOSProcessInfo();
+
     /* initialize the storage manager */
     initStorage();
 
index faa54d7..e472c5a 100644 (file)
 #ifdef TRACING
 
 #include "GetTime.h"
+#include "GetEnv.h"
 #include "Stats.h"
 #include "eventlog/EventLog.h"
 #include "Threads.h"
 #include "Printer.h"
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
 #ifdef DEBUG
 // debugging flags, set with +RTS -D<something>
 int DEBUG_sched;
@@ -284,6 +289,50 @@ void traceCapsetModify_ (EventTypeNum tag,
     }
 }
 
+void traceOSProcessInfo_(void) {
+    if (eventlog_enabled) {
+        postCapsetModifyEvent(EVENT_OSPROCESS_PID,
+                              CAPSET_OSPROCESS_DEFAULT,
+                              getpid());
+
+#if !defined(cygwin32_HOST_OS) && !defined (mingw32_HOST_OS)
+/* Windows has no strong concept of process heirarchy, so no getppid().
+ * In any case, this trace event is mainly useful for tracing programs
+ * that use 'forkProcess' which Windows doesn't support anyway.
+ */
+        postCapsetModifyEvent(EVENT_OSPROCESS_PPID,
+                              CAPSET_OSPROCESS_DEFAULT,
+                              getppid());
+#endif
+        {
+            char buf[256];
+            snprintf(buf, sizeof(buf), "GHC-%s %s", ProjectVersion, RtsWay);
+            postCapsetStrEvent(EVENT_RTS_IDENTIFIER,
+                               CAPSET_OSPROCESS_DEFAULT,
+                               buf);
+        }
+        {
+            int argc = 0; char **argv;
+            getFullProgArgv(&argc, &argv);
+            if (argc != 0) {
+                postCapsetVecEvent(EVENT_PROGRAM_ARGS,
+                                   CAPSET_OSPROCESS_DEFAULT,
+                                   argc, argv);
+            }
+        }
+        {
+            int envc = 0; char **envv;
+            getProgEnvv(&envc, &envv);
+            if (envc != 0) {
+                postCapsetVecEvent(EVENT_PROGRAM_ENV,
+                                   CAPSET_OSPROCESS_DEFAULT,
+                                   envc, envv);
+            }
+            freeProgEnvv(envc, envv);
+        }
+    }
+}
+
 void traceEvent_ (Capability *cap, EventTypeNum tag)
 {
 #ifdef DEBUG
index f253280..1544971 100644 (file)
@@ -182,6 +182,8 @@ void traceCapsetModify_ (EventTypeNum tag,
                          CapsetID capset,
                          StgWord32 other);
 
+void traceOSProcessInfo_ (void);
+
 #else /* !TRACING */
 
 #define traceSchedEvent(cap, tag, tso, other) /* nothing */
@@ -194,6 +196,7 @@ void traceCapsetModify_ (EventTypeNum tag,
 #define traceThreadStatus(class, tso) /* nothing */
 #define traceEventStartup_(n_caps) /* nothing */
 #define traceCapsetModify_(tag, capset, other) /* nothing */
+#define traceOSProcessInfo_() /* nothing */
 
 #endif /* TRACING */
 
@@ -469,6 +472,13 @@ INLINE_HEADER void traceCapsetRemoveCap(CapsetID capset STG_UNUSED,
     dtraceCapsetRemoveCap(capset, capno);
 }
 
+INLINE_HEADER void traceOSProcessInfo(void)
+{
+    traceOSProcessInfo_();
+    /* Note: no DTrace equivalent because all this OS process info
+     * is available to DTrace directly */
+}
+
 #include "EndPrivate.h"
 
 #endif /* TRACE_H */
index b5c2ef6..abfb4eb 100644 (file)
@@ -79,7 +79,12 @@ char *EventDesc[] = {
   [EVENT_CAPSET_CREATE]       = "Create capability set",
   [EVENT_CAPSET_DELETE]       = "Delete capability set",
   [EVENT_CAPSET_ASSIGN_CAP]   = "Add capability to capability set",
-  [EVENT_CAPSET_REMOVE_CAP]   = "Remove capability from capability set"
+  [EVENT_CAPSET_REMOVE_CAP]   = "Remove capability from capability set",
+  [EVENT_RTS_IDENTIFIER]      = "RTS name and version",
+  [EVENT_PROGRAM_ARGS]        = "Program arguments",
+  [EVENT_PROGRAM_ENV]         = "Program environment variables",
+  [EVENT_OSPROCESS_PID]       = "Process ID",
+  [EVENT_OSPROCESS_PPID]      = "Parent process ID"
 };
 
 // Event type. 
@@ -284,6 +289,12 @@ initEventLogging(void)
                 sizeof(EventCapsetID) + sizeof(EventCapNo);
             break;
 
+        case EVENT_OSPROCESS_PID:   // (cap, pid)
+        case EVENT_OSPROCESS_PPID:
+            eventTypes[t].size =
+                sizeof(EventCapsetID) + sizeof(StgWord32);
+            break;
+
         case EVENT_SHUTDOWN:        // (cap)
         case EVENT_REQUEST_SEQ_GC:  // (cap)
         case EVENT_REQUEST_PAR_GC:  // (cap)
@@ -297,6 +308,9 @@ initEventLogging(void)
 
         case EVENT_LOG_MSG:          // (msg)
         case EVENT_USER_MSG:         // (msg)
+        case EVENT_RTS_IDENTIFIER:   // (capset, str)
+        case EVENT_PROGRAM_ARGS:     // (capset, strvec)
+        case EVENT_PROGRAM_ENV:      // (capset, strvec)
             eventTypes[t].size = 0xffff;
             break;
 
@@ -496,6 +510,12 @@ void postCapsetModifyEvent (EventTypeNum tag,
         postCapNo(&eventBuf, other /* capno */);
         break;
     }
+    case EVENT_OSPROCESS_PID:   // (capset, pid)
+    case EVENT_OSPROCESS_PPID:  // (capset, parent_pid)
+    {
+        postWord32(&eventBuf, other);
+        break;
+    }
     default:
         barf("postCapsetModifyEvent: unknown event tag %d", tag);
     }
@@ -503,6 +523,70 @@ void postCapsetModifyEvent (EventTypeNum tag,
     RELEASE_LOCK(&eventBufMutex);
 }
 
+void postCapsetStrEvent (EventTypeNum tag,
+                         EventCapsetID capset,
+                         char *msg)
+{
+    int strsize = strlen(msg);
+    int size = strsize + sizeof(EventCapsetID)
+
+    ACQUIRE_LOCK(&eventBufMutex);
+
+    if (!hasRoomForVariableEvent(&eventBuf, size)){
+        printAndClearEventBuf(&eventBuf);
+
+        if (!hasRoomForVariableEvent(&eventBuf, size)){
+            // Event size exceeds buffer size, bail out:
+            RELEASE_LOCK(&eventBufMutex);
+            return;
+        }
+    }
+
+    postEventHeader(&eventBuf, tag);
+    postPayloadSize(&eventBuf, size);
+    postCapsetID(&eventBuf, capset);
+
+    postBuf(&eventBuf, (StgWord8*) msg, strsize);
+
+    RELEASE_LOCK(&eventBufMutex);
+}
+
+void postCapsetVecEvent (EventTypeNum tag,
+                         EventCapsetID capset,
+                         int argc,
+                         char *argv[])
+{
+    int i, size = sizeof(EventCapsetID);
+
+    for (i = 0; i < argc; i++) {
+        // 1 + strlen to account for the trailing \0, used as separator
+        size += 1 + strlen(argv[i]);
+    }
+
+    ACQUIRE_LOCK(&eventBufMutex);
+
+    if (!hasRoomForVariableEvent(&eventBuf, size)){
+        printAndClearEventBuf(&eventBuf);
+
+        if(!hasRoomForVariableEvent(&eventBuf, size)){
+            // Event size exceeds buffer size, bail out:
+            RELEASE_LOCK(&eventBufMutex);
+            return;
+        }
+    }
+
+    postEventHeader(&eventBuf, tag);
+    postPayloadSize(&eventBuf, size);
+    postCapsetID(&eventBuf, capset);
+
+    for( i = 0; i < argc; i++ ) {
+        // again, 1 + to account for \0
+        postBuf(&eventBuf, (StgWord8*) argv[i], 1 + strlen(argv[i]));
+    }
+
+    RELEASE_LOCK(&eventBufMutex);
+}
+
 void
 postEvent (Capability *cap, EventTypeNum tag)
 {
index 116b532..602ac2c 100644 (file)
@@ -54,6 +54,21 @@ void postCapsetModifyEvent (EventTypeNum tag,
                             EventCapsetID capset,
                             StgWord32 other);
 
+/*
+ * Post a capability set event with a string payload
+ */
+void postCapsetStrEvent (EventTypeNum tag,
+                         EventCapsetID capset,
+                         char *msg);
+
+/*
+ * Post a capability set event with several strings payload
+ */
+void postCapsetVecEvent (EventTypeNum tag,
+                         EventCapsetID capset,
+                         int argc,
+                         char *msg[]);
+
 #else /* !TRACING */
 
 INLINE_HEADER void postSchedEvent (Capability *cap  STG_UNUSED,
index a236945..38ddbc0 100644 (file)
@@ -295,6 +295,7 @@ rts/RtsMain_HC_OPTS += -optc-O0
 
 rts/RtsMessages_CC_OPTS += -DProjectVersion=\"$(ProjectVersion)\"
 rts/RtsUtils_CC_OPTS += -DProjectVersion=\"$(ProjectVersion)\"
+rts/Trace_CC_OPTS += -DProjectVersion=\"$(ProjectVersion)\"
 #
 rts/RtsUtils_CC_OPTS += -DHostPlatform=\"$(HOSTPLATFORM)\"
 rts/RtsUtils_CC_OPTS += -DHostArch=\"$(HostArch_CPP)\"
diff --git a/rts/posix/GetEnv.c b/rts/posix/GetEnv.c
new file mode 100644 (file)
index 0000000..4d5c7e2
--- /dev/null
@@ -0,0 +1,44 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team 2011
+ *
+ * Access to the process environment variables
+ *
+ * ---------------------------------------------------------------------------*/
+
+#include "Rts.h"
+#include "GetEnv.h"
+
+#if defined(darwin_HOST_OS)
+
+/* While the "extern char** environ" var does exist on OSX, it is not
+ * available to shared libs. See ghc ticket #2458 and
+ * http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man7/environ.7.html
+ */
+#include <crt_externs.h>
+
+static char** get_environ(void) { return *(_NSGetEnviron()); }
+
+#else
+
+/* On proper unix systems the environ is just a global var.
+ */
+extern char** environ;
+static char** get_environ(void) { return environ; }
+
+#endif
+
+
+void getProgEnvv(int *out_envc, char **out_envv[]) {
+    int envc;
+    char **environ = get_environ();
+    
+    for (envc = 0; environ[envc] != NULL; envc++) {};
+
+    *out_envc = envc;
+    *out_envv = environ;
+}
+
+void freeProgEnvv(int envc STG_UNUSED, char *envv[] STG_UNUSED) {
+    /* nothing */
+}
diff --git a/rts/win32/GetEnv.c b/rts/win32/GetEnv.c
new file mode 100644 (file)
index 0000000..b8a4395
--- /dev/null
@@ -0,0 +1,61 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) The GHC Team 2011
+ *
+ * Access to the process environment variables
+ *
+ * ---------------------------------------------------------------------------*/
+
+#include "Rts.h"
+#include "GetEnv.h"
+
+#include <windows.h>
+
+/* Windows does it differently, though arguably the most sanely.
+ * GetEnvironmentStrings() returns a pointer to a block of
+ * environment vars with a double null terminator:
+ *   Var1=Value1\0
+ *   Var2=Value2\0
+ *   ...
+ *   VarN=ValueN\0\0 
+ * But because everyone else (ie POSIX) uses a vector of strings, we convert
+ * to that format. Fortunately this is just a matter of making an array of
+ * offsets into the environment block.
+ *
+ * Note that we have to call FreeEnvironmentStrings() at the end.
+ *
+ */
+void getProgEnvv(int *out_envc, char **out_envv[]) {
+    int envc, i;
+    char *env;
+    char *envp;
+    char **envv;
+
+    /* For now, use the 'A'nsi not 'W'ide variant.
+       Note: corresponding Free below must use the same 'A'/'W' variant. */
+    env = GetEnvironmentStringsA();
+
+    envc = 0;
+    for (envp = env; *envp != 0; envp += strlen(envp) + 1) {
+        envc++;
+    }
+
+    envv = stgMallocBytes(sizeof(char*) * (envc+1));
+
+    i = 0;
+    for (envp = env; *envp != NULL; envp += strlen(envp) + 1) {
+        envv[i] = envp;
+        i++;
+    }
+    /* stash whole env in last+1 entry */
+    envv[envc] = env;
+
+    *out_envc = envc;
+    *out_envv = envv;
+}
+
+void freeProgEnvv(int envc, char *envv[]) {
+    /* we stashed the win32 env block in the last+1 entry */
+    FreeEnvironmentStringsA(envv[envc]);
+    stgFree(envv);
+}