1 /* darkstat 3
2  * copyright (c) 2012-2014 Emil Mikulic.
3  *
4  * now.c: a cache of the current time.
5  *
6  * Permission to use, copy, modify, and distribute this file for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include "err.h"
19 #include "now.h"
20 #include "str.h"
21 
22 #include <assert.h>
23 #include <string.h>
24 #include <time.h>
25 
26 #if defined(__MACH__) && !defined(__gnu_hurd__)
27 /* Fake up clock_gettime() on OS X. */
28 #  include <sys/time.h>
29 #  include <inttypes.h>
30 #  include <mach/mach.h>
31 #  include <mach/mach_time.h>
32 
33    typedef int clockid_t;
34 #  define CLOCK_REALTIME 0
35 #  define CLOCK_MONOTONIC 1
36 
37    static uint64_t mono_first = 0;
38 
clock_gettime(clockid_t clk_id,struct timespec * tp)39    int clock_gettime(clockid_t clk_id, struct timespec *tp) {
40       if (clk_id == CLOCK_REALTIME) {
41          struct timeval tv;
42          gettimeofday(&tv, NULL);
43          tp->tv_sec = tv.tv_sec;
44          tp->tv_nsec = tv.tv_usec * 1000;
45          return 0;
46       }
47       if (clk_id == CLOCK_MONOTONIC) {
48          uint64_t t = mach_absolute_time();
49          mach_timebase_info_data_t timebase;
50          mach_timebase_info(&timebase);
51          if (!mono_first) {
52             mono_first = t;
53          }
54          uint64_t tdiff = (t - mono_first) * timebase.numer / timebase.denom;
55          tp->tv_sec = tdiff / 1000000000;
56          tp->tv_nsec = tdiff % 1000000000;
57          return 0;
58       }
59       return -1;
60    }
61 #endif  /* __MACH__ */
62 
63 static struct timespec clock_real, clock_mono;
64 static int now_initialized = 0;
65 
now_real(void)66 time_t now_real(void) {
67    assert(now_initialized);
68    return clock_real.tv_sec;
69 }
70 
now_mono(void)71 time_t now_mono(void) {
72    assert(now_initialized);
73    return clock_mono.tv_sec;
74 }
75 
before(const struct timespec * a,const struct timespec * b)76 static int before(const struct timespec *a, const struct timespec *b) {
77    if (a->tv_sec < b->tv_sec)
78       return 1;
79    if (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec)
80       return 1;
81    return 0;
82 }
83 
warn_backwards(const char * name,const struct timespec * const t0,const struct timespec * const t1)84 static void warn_backwards(const char *name,
85                            const struct timespec * const t0,
86                            const struct timespec * const t1) {
87    verbosef("%s clock went backwards from %lld.%09lld to %lld.%09lld",
88             name,
89             (lld)t0->tv_sec,
90             (lld)t0->tv_nsec,
91             (lld)t1->tv_sec,
92             (lld)t1->tv_nsec);
93 }
94 
clock_update(const clockid_t clk_id,struct timespec * dest,const char * name)95 static void clock_update(const clockid_t clk_id,
96                          struct timespec *dest,
97                          const char *name) {
98    struct timespec t;
99 
100    clock_gettime(clk_id, &t);
101    if (now_initialized && before(&t, dest)) {
102       warn_backwards(name, &t, dest);
103    }
104    memcpy(dest, &t, sizeof(t));
105 }
106 
all_clocks_update(void)107 static void all_clocks_update(void) {
108    clock_update(CLOCK_REALTIME,  &clock_real, "realtime");
109    clock_update(CLOCK_MONOTONIC, &clock_mono, "monotonic");
110 }
111 
now_init(void)112 void now_init(void) {
113    assert(!now_initialized);
114    all_clocks_update();
115    now_initialized = 1;
116 }
117 
now_update(void)118 void now_update(void) {
119    assert(now_initialized);
120    all_clocks_update();
121 }
122 
mono_to_real(const int64_t t)123 time_t mono_to_real(const int64_t t) {
124    assert(now_initialized);
125    return (time_t)(t - (int64_t)clock_mono.tv_sec + (int64_t)clock_real.tv_sec);
126 }
127 
real_to_mono(const time_t t)128 int64_t real_to_mono(const time_t t) {
129    assert(now_initialized);
130    return (int64_t)(t - clock_real.tv_sec + clock_mono.tv_sec);
131 }
132 
timer_start(struct timespec * t)133 void timer_start(struct timespec *t) {
134    clock_gettime(CLOCK_MONOTONIC, t);
135 }
136 
ts_diff(const struct timespec * const a,const struct timespec * const b)137 static int64_t ts_diff(const struct timespec * const a,
138                        const struct timespec * const b) {
139    return (int64_t)(a->tv_sec - b->tv_sec) * 1000000000 +
140           a->tv_nsec - b->tv_nsec;
141 }
142 
timer_stop(const struct timespec * const t0,const int64_t nsec,const char * warning)143 void timer_stop(const struct timespec * const t0,
144                 const int64_t nsec,
145                 const char *warning) {
146    struct timespec t1;
147    int64_t diff;
148 
149    clock_gettime(CLOCK_MONOTONIC, &t1);
150    if (before(&t1, t0)) {
151       warn_backwards("monotonic timer", t0, &t1);
152       return;
153    }
154    diff = ts_diff(&t1, t0);
155    if (diff > nsec) {
156       warnx("%s (took %lld nsec, over threshold of %lld nsec)",
157             warning,
158             (lld)diff,
159             (lld)nsec);
160    }
161 }
162 
163 /* vim:set ts=3 sw=3 tw=80 et: */
164