New functions to get kernel thread Id + serialisable task Id
authorDuncan Coutts <duncan@well-typed.com>
Tue, 3 Jul 2012 18:18:46 +0000 (19:18 +0100)
committerMikolaj Konarski <mikolaj@well-typed.com>
Fri, 6 Jul 2012 22:28:19 +0000 (00:28 +0200)
On most platforms the userspace thread type (e.g. pthread_t) and kernel
thread id are different. Normally we don't care about kernel thread Ids,
but some system tools for tracing/profiling etc report kernel ids.
For example Solaris and OSX's DTrace and Linux's perf tool report kernel
thread ids. To be able to match these up with RTS's OSThread we need a
way to get at the kernel thread, so we add a new function for to do just
that (the implementation is system-dependent).

Additionally, strictly speaking the OSThreadId type, used as task ids,
is not a serialisable representation. On unix OSThreadId is a typedef for
pthread_t, but pthread_t is not guaranteed to be a numeric type.
Indeed on some systems pthread_t is a pointer and in principle it
could be a structure type. So we add another new function to get a
serialisable representation of an OSThreadId. This is only for use
in log files. We use the function to serialise an id of a task,
with the extra feature that it works in non-threaded builds
by always returning 1.

includes/rts/OSThreads.h
rts/Task.c
rts/Task.h
rts/posix/OSThreads.c
rts/win32/OSThreads.c

index cf72e1d..260aac1 100644 (file)
@@ -15,7 +15,7 @@
 #ifndef RTS_OSTHREADS_H
 #define RTS_OSTHREADS_H
 
-#if defined(THREADED_RTS) /* to the end */
+#if defined(THREADED_RTS) /* to near the end */
 
 #if defined(HAVE_PTHREAD_H) && !defined(mingw32_HOST_OS)
 
@@ -222,6 +222,29 @@ int forkOS_createThread ( HsStablePtr entry );
 // Returns the number of processor cores in the machine
 //
 nat getNumberOfProcessors (void);
-#endif
+
+//
+// Support for getting at the kernel thread Id for tracing/profiling.
+//
+// This stuff is optional and only used for tracing/profiling purposes, to
+// match up thread ids recorded by other tools. For example, on Linux and OSX
+// the pthread_t type is not the same as the kernel thread id, and system
+// profiling tools like Linux perf, and OSX's DTrace use the kernel thread Id.
+// So if we want to match up RTS tasks with kernel threads recorded by these
+// tools then we need to know the kernel thread Id, and this must be a separate
+// type from the OSThreadId.
+//
+// If the feature cannot be supported on an OS, it is OK to always return 0.
+// In particular it would almost certaily be meaningless on systems not using
+// a 1:1 threading model.
+
+// We use a common serialisable representation on all OSs
+// This is ok for Windows, OSX and Linux.
+typedef StgWord64 KernelThreadId;
+
+// Get the current kernel thread id
+KernelThreadId kernelThreadId (void);
+
+#endif /* CMINUSMINUS */
 
 #endif /* RTS_OSTHREADS_H */
index 125000b..b6092e5 100644 (file)
@@ -389,7 +389,10 @@ workerTaskStop (Task *task)
 #endif
 
 #ifdef DEBUG
-
+// We don't replace this function with serialisableTaskId,
+// because debug prints as pointers are more readable than random
+// 64-bit intergers (especially on 32-bit architectures)
+// and because we want to use this function also for non-treaded RTS.
 static void *taskId(Task *task)
 {
 #ifdef THREADED_RTS
index ab47a07..7c9c86e 100644 (file)
@@ -159,7 +159,6 @@ isBoundTask (Task *task)
     return (task->incall->tso != NULL);
 }
 
-
 // Linked list of all tasks.
 //
 extern Task *all_tasks;
@@ -275,6 +274,44 @@ setMyTask (Task *task)
 #endif
 }
 
+// Tasks are identified by their OS thread ID, which can be serialised
+// to StgWord64, as defined below.
+typedef StgWord64 TaskId;
+
+// Get a unique serialisable representation for a task id.
+//
+// It's only unique within the process. For example if they are emitted in a
+// log file then it is suitable to work out which log entries are releated.
+//
+// This is needed because OSThreadId is an opaque type
+// and in practice on some platforms it is a pointer type.
+//
+#if defined(THREADED_RTS)
+INLINE_HEADER TaskId serialiseTaskId (OSThreadId taskID) {
+#if defined(freebsd_HOST_OS) || defined(darwin_HOST_OS)
+    // Here OSThreadId is a pthread_t and pthread_t is a pointer, but within
+    // the process we can still use that pointer value as a unique id.
+    return (TaskId) taskID
+#else
+    // On Windows, Linux and others it's an integral type to start with.
+    return taskID;
+#endif
+}
+#endif
+
+//
+// Get a serialisable Id for the Task's OS thread
+// Needed mainly for logging since the OSThreadId is an opaque type
+INLINE_HEADER TaskId
+serialisableTaskId (Task *task STG_UNUSED)
+{
+#if defined(THREADED_RTS)
+    return serialiseTaskId(task->id);
+#else
+    return 1;
+#endif
+}
+
 #include "EndPrivate.h"
 
 #endif /* TASK_H */
index c294548..5fd0998 100644 (file)
 
 #include "Rts.h"
 
+#if defined(linux_HOST_OS)
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#endif
+
 #if defined(THREADED_RTS)
 #include "RtsUtils.h"
 #include "Task.h"
@@ -312,4 +318,24 @@ nat getNumberOfProcessors (void)
     return 1;
 }
 
+#endif /* defined(THREADED_RTS) */
+
+KernelThreadId kernelThreadId (void)
+{
+#if defined(linux_HOST_OS)
+    pid_t tid = syscall(SYS_gettid); // no really, see man gettid
+    return tid;
+
+#elif defined(freebsd_HOST_OS)
+    return pthread_getthreadid_np();
+
+#elif defined(darwin_HOST_OS)
+    uint64_t ktid;
+    pthread_threadid_np(NULL, &ktid);
+    return ktid;
+
+#else
+    // unsupported
+    return 0;
 #endif
+}
index 818f97b..d0d97b3 100644 (file)
@@ -10,9 +10,9 @@
 #define _WIN32_WINNT 0x0500
 
 #include "Rts.h"
+#include <windows.h>
 #if defined(THREADED_RTS)
 #include "RtsUtils.h"
-#include <windows.h>
 
 /* For reasons not yet clear, the entire contents of process.h is protected 
  * by __STRICT_ANSI__ not being defined.
@@ -314,3 +314,9 @@ nat getNumberOfProcessors (void)
 }
 
 #endif /* !defined(THREADED_RTS) */
+
+KernelThreadId kernelThreadId (void)
+{
+    DWORD tid = GetCurrentThreadId();
+    return tid;
+}