diff mbox

[uclibc-ng-devel] pthread_getcpuclockid.c: fix clockid calculation

Message ID CAKo4hFKbWfhqYino5HjDPwKNzW0nPvF1CWqKrn=utMeEieiVuA@mail.gmail.com
State Superseded
Headers show

Commit Message

Sergey Korolev April 25, 2017, 11:22 p.m. UTC
Current uClibc-ng version incorrectly calculates clockid
in pthread_getcpuclockid (at least for modern kernels). The simplest test
program

#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

int main()
{
    clockid_t clk;
    struct timespec ts;
    const int err = pthread_getcpuclockid(pthread_self(), &clk);

    if (err != 0) {
        errno = err;
        perror("pthread_getcpuclockid");
        return EXIT_FAILURE;
    }

    if (clock_gettime(clk, &ts) == -1) {
        perror("clock_gettime");
        return EXIT_FAILURE;
    }

    printf("Thread time is %lu.%06lu.\n",
           ts.tv_sec,
           ts.tv_nsec / 1000);

    return EXIT_SUCCESS;
}

fails with

clock_gettime: Invalid argument

Tested on Linux 3.4 / MIPS built with GCC 5.4.0.

Other implementations, for example musl, use a simple calculation
https://git.musl-libc.org/cgit/musl/tree/src/thread/pthread_getcpuclockid.c

Looks strange, but the official glibc repository
https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=nptl/pthread_getcpuclockid.c;hb=HEAD
has the same implementation as in uClibc-ng.

This fork
https://github.com/lattera/glibc/blob/master/nptl/sysdeps/unix/sysv/linux/pthread_getcpuclockid.c
use more accurate approach relying on __NR_clock_getres (defined in the
kernel since 2.6.24) with MAKE_THREAD_CPUCLOCK macro copied from the kernel
(
http://lxr.free-electrons.com/source/include/linux/posix-timers.h?v=2.6.24#L34,
Linux
4.10 has the same one).

I propose an intermediate solution: use MAKE_THREAD_CPUCLOCK like
computation of clockid when __NR_clock_getres defined and do a fallback to
an older implementation when not.

Comments

Sergey Korolev April 26, 2017, 4:51 a.m. UTC | #1
Sorry,

Latests official glibc in system dependent implementation for Linux simply
use MAKE_THREAD_CPUCLOCK
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/pthread_getcpuclockid.c;h=f5af64ea88b92e64ed86c4a9ca254bbceb103b29;hb=9fe3c80c7c3dbce34dadc7f0693d211fdd9a0b03

On Wed, Apr 26, 2017 at 2:22 AM, Sergey Korolev <s.korolev@ndmsystems.com>
wrote:

> Current uClibc-ng version incorrectly calculates clockid
> in pthread_getcpuclockid (at least for modern kernels). The simplest test
> program
>
> #include <time.h>
> #include <errno.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <pthread.h>
>
> int main()
> {
>     clockid_t clk;
>     struct timespec ts;
>     const int err = pthread_getcpuclockid(pthread_self(), &clk);
>
>     if (err != 0) {
>         errno = err;
>         perror("pthread_getcpuclockid");
>         return EXIT_FAILURE;
>     }
>
>     if (clock_gettime(clk, &ts) == -1) {
>         perror("clock_gettime");
>         return EXIT_FAILURE;
>     }
>
>     printf("Thread time is %lu.%06lu.\n",
>            ts.tv_sec,
>            ts.tv_nsec / 1000);
>
>     return EXIT_SUCCESS;
> }
>
> fails with
>
> clock_gettime: Invalid argument
>
> Tested on Linux 3.4 / MIPS built with GCC 5.4.0.
>
> Other implementations, for example musl, use a simple calculation
> https://git.musl-libc.org/cgit/musl/tree/src/thread/
> pthread_getcpuclockid.c
>
> Looks strange, but the official glibc repository
> https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=nptl/
> pthread_getcpuclockid.c;hb=HEAD
> has the same implementation as in uClibc-ng.
>
> This fork https://github.com/lattera/glibc/blob/master/
> nptl/sysdeps/unix/sysv/linux/pthread_getcpuclockid.c
> use more accurate approach relying on __NR_clock_getres (defined in the
> kernel since 2.6.24) with MAKE_THREAD_CPUCLOCK macro copied from the kernel
> (http://lxr.free-electrons.com/source/include/linux/
> posix-timers.h?v=2.6.24#L34, Linux 4.10 has the same one).
>
> I propose an intermediate solution: use MAKE_THREAD_CPUCLOCK like
> computation of clockid when __NR_clock_getres defined and do a fallback to
> an older implementation when not.
>
Waldemar Brodkorb April 27, 2017, 5:33 a.m. UTC | #2
Hi Sergey,
Sergey Korolev wrote,

> Current uClibc-ng version incorrectly calculates clockid
> in pthread_getcpuclockid (at least for modern kernels). The simplest test
> program
> 
> fails with
> 
> clock_gettime: Invalid argument
> 
> Tested on Linux 3.4 / MIPS built with GCC 5.4.0.

Thanks for the test case.
 
> Other implementations, for example musl, use a simple calculation
> https://git.musl-libc.org/cgit/musl/tree/src/thread/pthread_getcpuclockid.c
> 
> Looks strange, but the official glibc repository
> https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=nptl/
> pthread_getcpuclockid.c;hb=HEAD
> has the same implementation as in uClibc-ng.

uClibc imported NPTL from GNU libc in 2005. There are still many
similaraties.

Will you propose a new patch? ( I have read your second mail)

best regards
 Waldemar
diff mbox

Patch

From caebc4ced19c46725109d607c47a355d66da3097 Mon Sep 17 00:00:00 2001
From: Sergey Korolev <s.korolev@ndmsystems.com>
Date: Tue, 25 Apr 2017 02:14:59 +0300
Subject: [PATCH] pthread_getcpuclockid.c: fix clockid calculation

According to newer linux kernel sources (since 2.6.24)
MAKE_THREAD_CPUCLOCK macro should be used to get clockid.

The fix use MAKE_THREAD_CPUCLOCK-like computation when
__NR_clock_getres defined and do a fallback to an older
implementation when not.
---
 .../nptl/sysdeps/unix/sysv/linux/pthread_getcpuclockid.c    | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/libpthread/nptl/sysdeps/unix/sysv/linux/pthread_getcpuclockid.c b/libpthread/nptl/sysdeps/unix/sysv/linux/pthread_getcpuclockid.c
index ca3570f..0fe6a35 100644
--- a/libpthread/nptl/sysdeps/unix/sysv/linux/pthread_getcpuclockid.c
+++ b/libpthread/nptl/sysdeps/unix/sysv/linux/pthread_getcpuclockid.c
@@ -20,6 +20,8 @@ 
 #include <sys/time.h>
 #include <tls.h>
 
+#define CPUCLOCK_PERTHREAD_MASK	4
+#define CPUCLOCK_SCHED		2
 
 int
 pthread_getcpuclockid (
@@ -33,7 +35,13 @@  pthread_getcpuclockid (
     /* Not a valid thread handle.  */
     return ESRCH;
 
-#ifdef CLOCK_THREAD_CPUTIME_ID
+#ifdef __NR_clock_getres
+  *clockid = ((~(clockid_t) (pd->tid)) << CLOCK_IDFIELD_SIZE)
+    | CPUCLOCK_SCHED | CPUCLOCK_PERTHREAD_MASK;
+
+  return 0;
+#else
+# ifdef CLOCK_THREAD_CPUTIME_ID
   /* We need to store the thread ID in the CLOCKID variable together
      with a number identifying the clock.  We reserve the low 3 bits
      for the clock ID and the rest for the thread ID.  This is
@@ -49,8 +57,9 @@  pthread_getcpuclockid (
   *clockid = CLOCK_THREAD_CPUTIME_ID | (pd->tid << CLOCK_IDFIELD_SIZE);
 
   return 0;
-#else
+# else
   /* We don't have a timer for that.  */
   return ENOENT;
+# endif
 #endif
 }
-- 
2.7.4