From e8832eb9f05ca46d9315250c3baf7010eb0630a4 Mon Sep 17 00:00:00 2001 From: Duncan Coutts Date: Thu, 26 May 2011 18:44:41 +0100 Subject: [PATCH] Emit various bits of OS process info into the eventlog The process ID, parent process ID, rts name and version The program arguments and environment. --- includes/rts/EventLogFormat.h | 40 ++++++++++++++++++- rts/GetEnv.h | 23 +++++++++++ rts/RtsStartup.c | 3 ++ rts/Trace.c | 49 +++++++++++++++++++++++ rts/Trace.h | 10 +++++ rts/eventlog/EventLog.c | 86 ++++++++++++++++++++++++++++++++++++++++- rts/eventlog/EventLog.h | 15 +++++++ rts/ghc.mk | 1 + rts/posix/GetEnv.c | 44 +++++++++++++++++++++ rts/win32/GetEnv.c | 61 +++++++++++++++++++++++++++++ 10 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 rts/GetEnv.h create mode 100644 rts/posix/GetEnv.c create mode 100644 rts/win32/GetEnv.c diff --git a/includes/rts/EventLogFormat.h b/includes/rts/EventLogFormat.h index f3f56c9..1bbb2f0 100644 --- a/includes/rts/EventLogFormat.h +++ b/includes/rts/EventLogFormat.h @@ -112,6 +112,7 @@ #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) */ @@ -120,12 +121,40 @@ #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 /* @@ -152,6 +181,13 @@ */ #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 index 0000000..5e3d0cf --- /dev/null +++ b/rts/GetEnv.h @@ -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 */ diff --git a/rts/RtsStartup.c b/rts/RtsStartup.c index 0dacd61..c115701 100644 --- a/rts/RtsStartup.c +++ b/rts/RtsStartup.c @@ -153,6 +153,9 @@ hs_init(int *argc, char **argv[]) */ initScheduler(); + /* Trace some basic information about the process */ + traceOSProcessInfo(); + /* initialize the storage manager */ initStorage(); diff --git a/rts/Trace.c b/rts/Trace.c index faa54d7..e472c5a 100644 --- a/rts/Trace.c +++ b/rts/Trace.c @@ -15,11 +15,16 @@ #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 +#endif + #ifdef DEBUG // debugging flags, set with +RTS -D 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 diff --git a/rts/Trace.h b/rts/Trace.h index f253280..1544971 100644 --- a/rts/Trace.h +++ b/rts/Trace.h @@ -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 */ diff --git a/rts/eventlog/EventLog.c b/rts/eventlog/EventLog.c index b5c2ef6..abfb4eb 100644 --- a/rts/eventlog/EventLog.c +++ b/rts/eventlog/EventLog.c @@ -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) { diff --git a/rts/eventlog/EventLog.h b/rts/eventlog/EventLog.h index 116b532..602ac2c 100644 --- a/rts/eventlog/EventLog.h +++ b/rts/eventlog/EventLog.h @@ -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, diff --git a/rts/ghc.mk b/rts/ghc.mk index a236945..38ddbc0 100644 --- a/rts/ghc.mk +++ b/rts/ghc.mk @@ -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 index 0000000..4d5c7e2 --- /dev/null +++ b/rts/posix/GetEnv.c @@ -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 + +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 index 0000000..b8a4395 --- /dev/null +++ b/rts/win32/GetEnv.c @@ -0,0 +1,61 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team 2011 + * + * Access to the process environment variables + * + * ---------------------------------------------------------------------------*/ + +#include "Rts.h" +#include "GetEnv.h" + +#include + +/* 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); +} -- 1.7.10.4