clock_gettime.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /* clock_gettime -- Get current time from a POSIX clockid_t. Linux version.
  2. Copyright (C) 2003, 2004 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, write to the Free
  14. Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  15. 02111-1307 USA. */
  16. #include <sysdep.h>
  17. #include <errno.h>
  18. #include <time.h>
  19. #include "kernel-posix-cpu-timers.h"
  20. #include <bits/kernel-features.h>
  21. #define SYSCALL_GETTIME \
  22. retval = INLINE_SYSCALL (clock_gettime, 2, clock_id, tp); \
  23. break
  24. #ifdef __ASSUME_POSIX_TIMERS
  25. /* This means the REALTIME and MONOTONIC clock are definitely
  26. supported in the kernel. */
  27. # define SYSDEP_GETTIME \
  28. SYSDEP_GETTIME_CPUTIME \
  29. case CLOCK_REALTIME: \
  30. case CLOCK_MONOTONIC: \
  31. SYSCALL_GETTIME
  32. # define __libc_missing_posix_timers 0
  33. #elif defined __NR_clock_gettime
  34. /* Is the syscall known to exist? */
  35. int __libc_missing_posix_timers attribute_hidden;
  36. static inline int
  37. maybe_syscall_gettime (clockid_t clock_id, struct timespec *tp)
  38. {
  39. int e = EINVAL;
  40. if (!__libc_missing_posix_timers)
  41. {
  42. INTERNAL_SYSCALL_DECL (err);
  43. int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp);
  44. if (!INTERNAL_SYSCALL_ERROR_P (r, err))
  45. return 0;
  46. e = INTERNAL_SYSCALL_ERRNO (r, err);
  47. if (e == ENOSYS)
  48. {
  49. __libc_missing_posix_timers = 1;
  50. e = EINVAL;
  51. }
  52. }
  53. return e;
  54. }
  55. /* The REALTIME and MONOTONIC clock might be available. Try the
  56. syscall first. */
  57. # define SYSDEP_GETTIME \
  58. SYSDEP_GETTIME_CPUTIME \
  59. case CLOCK_REALTIME: \
  60. case CLOCK_MONOTONIC: \
  61. retval = maybe_syscall_gettime (clock_id, tp); \
  62. if (retval == 0) \
  63. break; \
  64. /* Fallback code. */ \
  65. if (retval == EINVAL && clock_id == CLOCK_REALTIME) \
  66. retval = realtime_gettime (tp); \
  67. else \
  68. { \
  69. __set_errno (retval); \
  70. retval = -1; \
  71. } \
  72. break;
  73. #endif
  74. #ifdef __NR_clock_gettime
  75. /* We handled the REALTIME clock here. */
  76. # define HANDLED_REALTIME 1
  77. # define HANDLED_CPUTIME 1
  78. # if __ASSUME_POSIX_CPU_TIMERS > 0
  79. # define SYSDEP_GETTIME_CPU SYSCALL_GETTIME
  80. # define SYSDEP_GETTIME_CPUTIME /* Default catches them too. */
  81. # else
  82. int __libc_missing_posix_cpu_timers attribute_hidden;
  83. static int
  84. maybe_syscall_gettime_cpu (clockid_t clock_id, struct timespec *tp)
  85. {
  86. int e = EINVAL;
  87. if (!__libc_missing_posix_cpu_timers)
  88. {
  89. INTERNAL_SYSCALL_DECL (err);
  90. int r = INTERNAL_SYSCALL (clock_gettime, err, 2, clock_id, tp);
  91. if (!INTERNAL_SYSCALL_ERROR_P (r, err))
  92. return 0;
  93. e = INTERNAL_SYSCALL_ERRNO (r, err);
  94. # ifndef __ASSUME_POSIX_TIMERS
  95. if (e == ENOSYS)
  96. {
  97. __libc_missing_posix_timers = 1;
  98. __libc_missing_posix_cpu_timers = 1;
  99. e = EINVAL;
  100. }
  101. else
  102. # endif
  103. {
  104. if (e == EINVAL)
  105. {
  106. /* Check whether the kernel supports CPU clocks at all.
  107. If not, record it for the future. */
  108. r = INTERNAL_SYSCALL (clock_getres, err, 2,
  109. MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED),
  110. NULL);
  111. if (INTERNAL_SYSCALL_ERROR_P (r, err))
  112. __libc_missing_posix_cpu_timers = 1;
  113. }
  114. }
  115. }
  116. return e;
  117. }
  118. # define SYSDEP_GETTIME_CPU \
  119. retval = maybe_syscall_gettime_cpu (clock_id, tp); \
  120. if (retval == 0) \
  121. break; \
  122. if (retval != EINVAL || !__libc_missing_posix_cpu_timers) \
  123. { \
  124. __set_errno (retval); \
  125. retval = -1; \
  126. break; \
  127. } \
  128. retval = -1 /* Otherwise continue on to the HP_TIMING version. */;
  129. static inline int
  130. maybe_syscall_gettime_cputime (clockid_t clock_id, struct timespec *tp)
  131. {
  132. return maybe_syscall_gettime_cpu
  133. (clock_id == CLOCK_THREAD_CPUTIME_ID
  134. ? MAKE_THREAD_CPUCLOCK (0, CPUCLOCK_SCHED)
  135. : MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED),
  136. tp);
  137. }
  138. # define SYSDEP_GETTIME_CPUTIME \
  139. case CLOCK_PROCESS_CPUTIME_ID: \
  140. case CLOCK_THREAD_CPUTIME_ID: \
  141. retval = maybe_syscall_gettime_cputime (clock_id, tp); \
  142. if (retval == 0) \
  143. break; \
  144. if (retval != EINVAL || !__libc_missing_posix_cpu_timers) \
  145. { \
  146. __set_errno (retval); \
  147. retval = -1; \
  148. break; \
  149. } \
  150. retval = hp_timing_gettime (clock_id, tp); \
  151. break;
  152. # if !HP_TIMING_AVAIL
  153. # define hp_timing_gettime(clock_id, tp) (__set_errno (EINVAL), -1)
  154. # endif
  155. # endif
  156. #endif
  157. #include <errno.h>
  158. #include <stdint.h>
  159. #include <time.h>
  160. #include <sys/time.h>
  161. #include <libc-internal.h>
  162. #include <ldsodefs.h>
  163. #if HP_TIMING_AVAIL
  164. /* Clock frequency of the processor. We make it a 64-bit variable
  165. because some jokers are already playing with processors with more
  166. than 4GHz. */
  167. static hp_timing_t freq;
  168. /* This function is defined in the thread library. */
  169. extern int __pthread_clock_gettime (clockid_t clock_id, hp_timing_t freq,
  170. struct timespec *tp)
  171. __attribute__ ((__weak__));
  172. static int
  173. hp_timing_gettime (clockid_t clock_id, struct timespec *tp)
  174. {
  175. hp_timing_t tsc;
  176. if (__builtin_expect (freq == 0, 0))
  177. {
  178. /* This can only happen if we haven't initialized the `freq'
  179. variable yet. Do this now. We don't have to protect this
  180. code against multiple execution since all of them should
  181. lead to the same result. */
  182. freq = __get_clockfreq ();
  183. if (__builtin_expect (freq == 0, 0))
  184. /* Something went wrong. */
  185. return -1;
  186. }
  187. if (clock_id != CLOCK_PROCESS_CPUTIME_ID
  188. && __pthread_clock_gettime != NULL)
  189. return __pthread_clock_gettime (clock_id, freq, tp);
  190. /* Get the current counter. */
  191. HP_TIMING_NOW (tsc);
  192. /* Compute the offset since the start time of the process. */
  193. tsc -= GL(dl_cpuclock_offset);
  194. /* Compute the seconds. */
  195. tp->tv_sec = tsc / freq;
  196. /* And the nanoseconds. This computation should be stable until
  197. we get machines with about 16GHz frequency. */
  198. tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq;
  199. return 0;
  200. }
  201. #endif
  202. static inline int
  203. realtime_gettime (struct timespec *tp)
  204. {
  205. struct timeval tv;
  206. int retval = gettimeofday (&tv, NULL);
  207. if (retval == 0)
  208. /* Convert into `timespec'. */
  209. TIMEVAL_TO_TIMESPEC (&tv, tp);
  210. return retval;
  211. }
  212. librt_hidden_proto (clock_gettime)
  213. /* Get current value of CLOCK and store it in TP. */
  214. int
  215. clock_gettime (clockid_t clock_id, struct timespec *tp)
  216. {
  217. int retval = -1;
  218. #ifndef HANDLED_REALTIME
  219. struct timeval tv;
  220. #endif
  221. switch (clock_id)
  222. {
  223. #ifdef SYSDEP_GETTIME
  224. SYSDEP_GETTIME;
  225. #endif
  226. #ifndef HANDLED_REALTIME
  227. case CLOCK_REALTIME:
  228. retval = gettimeofday (&tv, NULL);
  229. if (retval == 0)
  230. TIMEVAL_TO_TIMESPEC (&tv, tp);
  231. break;
  232. #endif
  233. default:
  234. #ifdef SYSDEP_GETTIME_CPU
  235. SYSDEP_GETTIME_CPU;
  236. #endif
  237. #if HP_TIMING_AVAIL
  238. if ((clock_id & ((1 << CLOCK_IDFIELD_SIZE) - 1))
  239. == CLOCK_THREAD_CPUTIME_ID)
  240. retval = hp_timing_gettime (clock_id, tp);
  241. else
  242. #endif
  243. __set_errno (EINVAL);
  244. break;
  245. #if HP_TIMING_AVAIL && !defined HANDLED_CPUTIME
  246. case CLOCK_PROCESS_CPUTIME_ID:
  247. retval = hp_timing_gettime (clock_id, tp);
  248. break;
  249. #endif
  250. }
  251. return retval;
  252. }
  253. librt_hidden_def (clock_gettime)