Implement CorUnix::GetThreadTimesInternal() for NetBSD-7.0
authorKamil Rytarowski <n54@gmx.com>
Thu, 21 Apr 2016 07:19:54 +0000 (09:19 +0200)
committerKamil Rytarowski <n54@gmx.com>
Fri, 22 Apr 2016 08:32:33 +0000 (10:32 +0200)
Extract manually time used for a thread (LWP). NetBSD doesn't track
time used separately in user/kernel for threads, it's only done for
processes.

/*
 * KERN_LWP structure. See notes on KERN_PROC2 about adding elements.
 */
struct kinfo_lwp {
// ...
        uint32_t l_rtime_sec;           /* STRUCT TIMEVAL: Real time. */
        uint32_t l_rtime_usec;          /* STRUCT TIMEVAL: Real time. */
// ...
};

  -- /usr/include/sys/sysctl.h

Use the kvm(3) interface for this task.

The current limitation is returning time used for the current thread, not
the one selected at will - as there is no clean conversion (or rather
extraction of "lwpid_t pt_lid" from the private pthread_t structure).

src/pal/src/thread/thread.cpp

index 0bef811..0a8e41c 100644 (file)
@@ -33,6 +33,13 @@ Abstract:
 #include "pal/environ.h"
 #include "pal/init.h"
 
+#if defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <kvm.h>
+#endif
+
 #include <signal.h>
 #include <pthread.h>
 #if HAVE_PTHREAD_NP_H
@@ -1331,6 +1338,7 @@ CorUnix::GetThreadTimesInternal(
     __int64 calcTime;
     BOOL retval = FALSE;
     const __int64 SECS_TO_NS = 1000000000; /* 10^9 */
+    const __int64 USECS_TO_NS = 1000;      /* 10^3 */
 
 #if HAVE_MACH_THREADS
     thread_basic_info resUsage;
@@ -1340,8 +1348,6 @@ CorUnix::GetThreadTimesInternal(
     IPalObject *pobjThread = NULL;
     mach_msg_type_number_t resUsage_count = THREAD_BASIC_INFO_COUNT;
 
-    const __int64 USECS_TO_NS = 1000;      /* 10^3 */
-
     pthrCurrent = InternalGetCurrentThread();
     palError = InternalGetThreadDataFromHandle(
         pthrCurrent,
@@ -1399,6 +1405,89 @@ CorUnix::GetThreadTimesInternal(
 
     goto GetThreadTimesInternalExit;
 
+#elif defined(__NetBSD__) && !HAVE_PTHREAD_GETCPUCLOCKID /* Currently unimplemented */
+
+    PAL_ERROR palError;
+    CPalThread *pThread;
+    CPalThread *pTargetThread;
+    IPalObject *pobjThread = NULL;
+    kvm_t *kd;
+    int cnt, nlwps;
+    struct kinfo_lwp *klwp;
+    int i;
+    bool found = false;
+
+    pThread = InternalGetCurrentThread();
+
+    palError = InternalGetThreadDataFromHandle(
+        pThread,
+        hThread,
+        0, // THREAD_GET_CONTEXT
+        &pTargetThread,
+        &pobjThread
+        );
+    if (palError != NO_ERROR)
+    {
+        ASSERT("Unable to get thread data from handle %p"
+              "thread\n", hThread);
+        SetLastError(ERROR_INTERNAL_ERROR);
+        goto SetTimesToZero;
+    }
+
+    kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
+    if (kd == NULL)
+    {
+        ASSERT("kvm_open(3) error");
+        SetLastError(ERROR_INTERNAL_ERROR);
+        goto SetTimesToZero;
+    }
+
+    pTargetThread->Lock(pThread);
+
+    klwp = kvm_getlwps(kd, getpid(), 0, sizeof(struct kinfo_lwp), &nlwps);
+    if (klwp == NULL || nlwps < 1)
+    {
+        kvm_close(kd);
+        ASSERT("Unable to get clock from %p thread\n", hThread);
+        SetLastError(ERROR_INTERNAL_ERROR);
+        pTargetThread->Unlock(pThread);
+        goto SetTimesToZero;
+    }
+
+    for (i = 0; i < nlwps; i++)
+    {
+        if (klwp[i].l_lid == THREADSilentGetCurrentThreadId())
+        {
+            found = true;
+            break;
+        }
+    }
+
+    if (!found)
+    {
+        kvm_close(kd);
+        ASSERT("Unable to get clock from %p thread\n", hThread);
+        SetLastError(ERROR_INTERNAL_ERROR);
+        pTargetThread->Unlock(pThread);
+        goto SetTimesToZero;
+    }
+
+    pTargetThread->Unlock(pThread);
+
+    kvm_close(kd);
+
+    calcTime = (__int64) klwp[i].l_rtime_sec * SECS_TO_NS;
+    calcTime += (__int64) klwp[i].l_rtime_usec * USECS_TO_NS;
+    lpUserTime->dwLowDateTime = (DWORD)calcTime;
+    lpUserTime->dwHighDateTime = (DWORD)(calcTime >> 32);
+
+    /* NetBSD as of (7.0) doesn't differentiate used time in user/kernel for lwp */
+    lpKernelTime->dwLowDateTime = 0;
+    lpKernelTime->dwHighDateTime = 0;
+
+    retval = TRUE;
+    goto GetThreadTimesInternalExit;
+
 #else //HAVE_MACH_THREADS
 
     PAL_ERROR palError;