1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <folly/ClockGettimeWrappers.h>
18 
19 #include <folly/CPortability.h>
20 #include <folly/Likely.h>
21 #include <folly/portability/Time.h>
22 
23 #include <chrono>
24 
25 #include <ctime>
26 
27 #ifndef _WIN32
28 #define _GNU_SOURCE 1
29 #include <dlfcn.h>
30 #endif
31 
32 namespace folly {
33 namespace chrono {
34 
clock_gettime_ns_fallback(clockid_t clock)35 static int64_t clock_gettime_ns_fallback(clockid_t clock) {
36   struct timespec ts;
37   int r = clock_gettime(clock, &ts);
38   if (UNLIKELY(r != 0)) {
39     // Mimic what __clock_gettime_ns does (even though this can be a legit
40     // value).
41     return -1;
42   }
43   std::chrono::nanoseconds result =
44       std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec);
45   return result.count();
46 }
47 
48 // Initialize with default behavior, which we might override on Linux hosts
49 // with VDSO support.
50 int (*clock_gettime)(clockid_t, timespec* ts) = &::clock_gettime;
51 int64_t (*clock_gettime_ns)(clockid_t) = &clock_gettime_ns_fallback;
52 
53 // In MSAN mode use glibc's versions as they are intercepted by the MSAN
54 // runtime which properly tracks memory initialization.
55 #if defined(FOLLY_HAVE_LINUX_VDSO) && !defined(FOLLY_SANITIZE_MEMORY)
56 
57 namespace {
58 
59 struct VdsoInitializer {
VdsoInitializerfolly::chrono::__anon3af241700111::VdsoInitializer60   VdsoInitializer() {
61     m_handle = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
62     if (!m_handle) {
63       return;
64     }
65 
66     void* p = dlsym(m_handle, "__vdso_clock_gettime");
67     if (p) {
68       folly::chrono::clock_gettime = (int (*)(clockid_t, timespec*))p;
69     }
70     p = dlsym(m_handle, "__vdso_clock_gettime_ns");
71     if (p) {
72       folly::chrono::clock_gettime_ns = (int64_t(*)(clockid_t))p;
73     }
74   }
75 
~VdsoInitializerfolly::chrono::__anon3af241700111::VdsoInitializer76   ~VdsoInitializer() {
77     if (m_handle) {
78       clock_gettime = &::clock_gettime;
79       clock_gettime_ns = &clock_gettime_ns_fallback;
80       dlclose(m_handle);
81     }
82   }
83 
84  private:
85   void* m_handle;
86 };
87 
88 const VdsoInitializer vdso_initializer;
89 } // namespace
90 
91 #endif
92 } // namespace chrono
93 } // namespace folly
94