fio: add minimal gui program
[fio.git] / gettime.c
1 /*
2  * Clock functions
3  */
4
5 #include <unistd.h>
6 #include <math.h>
7 #include <sys/time.h>
8 #include <time.h>
9
10 #include "fio.h"
11 #include "smalloc.h"
12
13 #include "hash.h"
14
15 #ifdef ARCH_HAVE_CPU_CLOCK
16 static unsigned long cycles_per_usec;
17 static unsigned long last_cycles;
18 #endif
19 static struct timeval last_tv;
20 static int last_tv_valid;
21
22 static struct timeval *fio_tv;
23 int fio_gtod_offload = 0;
24 int fio_gtod_cpu = -1;
25 static pthread_t gtod_thread;
26
27 enum fio_cs fio_clock_source = FIO_PREFERRED_CLOCK_SOURCE;
28
29 #ifdef FIO_DEBUG_TIME
30
31 #define HASH_BITS       8
32 #define HASH_SIZE       (1 << HASH_BITS)
33
34 static struct flist_head hash[HASH_SIZE];
35 static int gtod_inited;
36
37 struct gtod_log {
38         struct flist_head list;
39         void *caller;
40         unsigned long calls;
41 };
42
43 static struct gtod_log *find_hash(void *caller)
44 {
45         unsigned long h = hash_ptr(caller, HASH_BITS);
46         struct flist_head *entry;
47
48         flist_for_each(entry, &hash[h]) {
49                 struct gtod_log *log = flist_entry(entry, struct gtod_log,
50                                                                         list);
51
52                 if (log->caller == caller)
53                         return log;
54         }
55
56         return NULL;
57 }
58
59 static struct gtod_log *find_log(void *caller)
60 {
61         struct gtod_log *log = find_hash(caller);
62
63         if (!log) {
64                 unsigned long h;
65
66                 log = malloc(sizeof(*log));
67                 INIT_FLIST_HEAD(&log->list);
68                 log->caller = caller;
69                 log->calls = 0;
70
71                 h = hash_ptr(caller, HASH_BITS);
72                 flist_add_tail(&log->list, &hash[h]);
73         }
74
75         return log;
76 }
77
78 static void gtod_log_caller(void *caller)
79 {
80         if (gtod_inited) {
81                 struct gtod_log *log = find_log(caller);
82
83                 log->calls++;
84         }
85 }
86
87 static void fio_exit fio_dump_gtod(void)
88 {
89         unsigned long total_calls = 0;
90         int i;
91
92         for (i = 0; i < HASH_SIZE; i++) {
93                 struct flist_head *entry;
94                 struct gtod_log *log;
95
96                 flist_for_each(entry, &hash[i]) {
97                         log = flist_entry(entry, struct gtod_log, list);
98
99                         printf("function %p, calls %lu\n", log->caller,
100                                                                 log->calls);
101                         total_calls += log->calls;
102                 }
103         }
104
105         printf("Total %lu gettimeofday\n", total_calls);
106 }
107
108 static void fio_init gtod_init(void)
109 {
110         int i;
111
112         for (i = 0; i < HASH_SIZE; i++)
113                 INIT_FLIST_HEAD(&hash[i]);
114
115         gtod_inited = 1;
116 }
117
118 #endif /* FIO_DEBUG_TIME */
119
120 #ifdef FIO_DEBUG_TIME
121 void fio_gettime(struct timeval *tp, void *caller)
122 #else
123 void fio_gettime(struct timeval *tp, void fio_unused *caller)
124 #endif
125 {
126 #ifdef FIO_DEBUG_TIME
127         if (!caller)
128                 caller = __builtin_return_address(0);
129
130         gtod_log_caller(caller);
131 #endif
132         if (fio_tv) {
133                 memcpy(tp, fio_tv, sizeof(*tp));
134                 return;
135         }
136
137         switch (fio_clock_source) {
138         case CS_GTOD:
139                 gettimeofday(tp, NULL);
140                 break;
141         case CS_CGETTIME: {
142                 struct timespec ts;
143
144 #ifdef FIO_HAVE_CLOCK_MONOTONIC
145                 if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
146 #else
147                 if (clock_gettime(CLOCK_REALTIME, &ts) < 0) {
148 #endif
149                         log_err("fio: clock_gettime fails\n");
150                         assert(0);
151                 }
152
153                 tp->tv_sec = ts.tv_sec;
154                 tp->tv_usec = ts.tv_nsec / 1000;
155                 break;
156                 }
157 #ifdef ARCH_HAVE_CPU_CLOCK
158         case CS_CPUCLOCK: {
159                 unsigned long long usecs, t;
160
161                 t = get_cpu_clock();
162                 if (t < last_cycles) {
163                         dprint(FD_TIME, "CPU clock going back in time\n");
164                         t = last_cycles;
165                 }
166
167                 usecs = t / cycles_per_usec;
168                 tp->tv_sec = usecs / 1000000;
169                 tp->tv_usec = usecs % 1000000;
170                 last_cycles = t;
171                 break;
172                 }
173 #endif
174         default:
175                 log_err("fio: invalid clock source %d\n", fio_clock_source);
176                 break;
177         }
178
179         /*
180          * If Linux is using the tsc clock on non-synced processors,
181          * sometimes time can appear to drift backwards. Fix that up.
182          */
183         if (last_tv_valid) {
184                 if (tp->tv_sec < last_tv.tv_sec)
185                         tp->tv_sec = last_tv.tv_sec;
186                 else if (last_tv.tv_sec == tp->tv_sec &&
187                          tp->tv_usec < last_tv.tv_usec)
188                         tp->tv_usec = last_tv.tv_usec;
189         }
190         last_tv_valid = 1;
191         memcpy(&last_tv, tp, sizeof(*tp));
192 }
193
194 #ifdef ARCH_HAVE_CPU_CLOCK
195 static unsigned long get_cycles_per_usec(void)
196 {
197         struct timeval s, e;
198         unsigned long long c_s, c_e;
199
200         gettimeofday(&s, NULL);
201         c_s = get_cpu_clock();
202         do {
203                 unsigned long long elapsed;
204
205                 gettimeofday(&e, NULL);
206                 elapsed = utime_since(&s, &e);
207                 if (elapsed >= 10) {
208                         c_e = get_cpu_clock();
209                         break;
210                 }
211         } while (1);
212
213         return c_e - c_s;
214 }
215
216 static void calibrate_cpu_clock(void)
217 {
218         double delta, mean, S;
219         unsigned long avg, cycles[10];
220         int i, samples;
221
222         cycles[0] = get_cycles_per_usec();
223         S = delta = mean = 0.0;
224         for (i = 0; i < 10; i++) {
225                 cycles[i] = get_cycles_per_usec();
226                 delta = cycles[i] - mean;
227                 if (delta) {
228                         mean += delta / (i + 1.0);
229                         S += delta * (cycles[i] - mean);
230                 }
231         }
232
233         S = sqrt(S / (10 - 1.0));
234
235         samples = avg = 0;
236         for (i = 0; i < 10; i++) {
237                 double this = cycles[i];
238
239                 if ((fmax(this, mean) - fmin(this, mean)) > S)
240                         continue;
241                 samples++;
242                 avg += this;
243         }
244
245         S /= 10.0;
246         mean /= 10.0;
247
248         for (i = 0; i < 10; i++)
249                 dprint(FD_TIME, "cycles[%d]=%lu\n", i, cycles[i] / 10);
250
251         avg /= (samples * 10);
252         dprint(FD_TIME, "avg: %lu\n", avg);
253         dprint(FD_TIME, "mean=%f, S=%f\n", mean, S);
254
255         cycles_per_usec = avg;
256
257 }
258 #else
259 static void calibrate_cpu_clock(void)
260 {
261 }
262 #endif
263
264 void fio_clock_init(void)
265 {
266         last_tv_valid = 0;
267         calibrate_cpu_clock();
268 }
269
270 void fio_gtod_init(void)
271 {
272         fio_tv = smalloc(sizeof(struct timeval));
273         assert(fio_tv);
274 }
275
276 static void fio_gtod_update(void)
277 {
278         gettimeofday(fio_tv, NULL);
279 }
280
281 static void *gtod_thread_main(void *data)
282 {
283         struct fio_mutex *mutex = data;
284
285         fio_mutex_up(mutex);
286
287         /*
288          * As long as we have jobs around, update the clock. It would be nice
289          * to have some way of NOT hammering that CPU with gettimeofday(),
290          * but I'm not sure what to use outside of a simple CPU nop to relax
291          * it - we don't want to lose precision.
292          */
293         while (threads) {
294                 fio_gtod_update();
295                 nop;
296         }
297
298         return NULL;
299 }
300
301 int fio_start_gtod_thread(void)
302 {
303         struct fio_mutex *mutex;
304         pthread_attr_t attr;
305         int ret;
306
307         mutex = fio_mutex_init(0);
308         if (!mutex)
309                 return 1;
310
311         pthread_attr_init(&attr);
312         pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
313         ret = pthread_create(&gtod_thread, &attr, gtod_thread_main, NULL);
314         pthread_attr_destroy(&attr);
315         if (ret) {
316                 log_err("Can't create gtod thread: %s\n", strerror(ret));
317                 goto err;
318         }
319
320         ret = pthread_detach(gtod_thread);
321         if (ret) {
322                 log_err("Can't detatch gtod thread: %s\n", strerror(ret));
323                 goto err;
324         }
325
326         dprint(FD_MUTEX, "wait on startup_mutex\n");
327         fio_mutex_down(mutex);
328         dprint(FD_MUTEX, "done waiting on startup_mutex\n");
329 err:
330         fio_mutex_remove(mutex);
331         return ret;
332 }