f8b2fc9dc19661976bf714fcef6ccfb2b659cd35
[platform/upstream/rpm.git] / rpmio / rpmsw.c
1 /** \ingroup rpmio
2  * \file rpmio/rpmsw.c
3  */
4
5 #include "system.h"
6 #include <rpmsw.h>
7 #include "debug.h"
8
9 #if defined(__LCLINT__)
10 /*@-exportheader@*/
11 extern int nanosleep(const struct timespec *__requested_time,
12                 /*@out@*/ /*@null@*/ struct timespec *__remaining)
13         /*@globals errno @*/
14         /*@modifies *__remaining, errno @*/;
15 /*@=exportheader@*/
16 #endif
17
18 /*@unchecked@*/
19 static rpmtime_t rpmsw_overhead = 0;
20
21 /*@unchecked@*/
22 static rpmtime_t rpmsw_cycles = 1;
23
24 /*@unchecked@*/
25 static int rpmsw_type = 0;
26
27 /*@unchecked@*/
28 static int rpmsw_initialized = 0;
29
30 #if defined(__i386__)
31 /* Swiped from glibc-2.3.2 sysdeps/i386/i686/hp-timing.h */
32
33 #define HP_TIMING_ZERO(Var)     (Var) = (0)
34 #define HP_TIMING_NOW(Var)      __asm__ __volatile__ ("rdtsc" : "=A" (Var))
35
36 /* It's simple arithmetic for us.  */
37 #define HP_TIMING_DIFF(Diff, Start, End)        (Diff) = ((End) - (Start))
38
39 /* We have to jump through hoops to get this correctly implemented.  */
40 #define HP_TIMING_ACCUM(Sum, Diff) \
41   do {                                                                        \
42     char __not_done;                                                          \
43     hp_timing_t __oldval = (Sum);                                             \
44     hp_timing_t __diff = (Diff) - GL(dl_hp_timing_overhead);                  \
45     do                                                                        \
46       {                                                                       \
47         hp_timing_t __newval = __oldval + __diff;                             \
48         int __temp0, __temp1;                                                 \
49         __asm__ __volatile__ ("xchgl %4, %%ebx\n\t"                           \
50                               "lock; cmpxchg8b %1\n\t"                        \
51                               "sete %0\n\t"                                   \
52                               "movl %4, %%ebx"                                \
53                               : "=q" (__not_done), "=m" (Sum),                \
54                                 "=A" (__oldval), "=c" (__temp0),              \
55                                 "=SD" (__temp1)                               \
56                               : "1" (Sum), "2" (__oldval),                    \
57                                 "3" (__newval >> 32),                         \
58                                 "4" (__newval & 0xffffffff)                   \
59                               : "memory");                                    \
60       }                                                                       \
61     while (__not_done);                                                       \
62   } while (0)
63
64 /* No threads, no extra work.  */
65 #define HP_TIMING_ACCUM_NT(Sum, Diff)   (Sum) += (Diff)
66
67 /* Print the time value.  */
68 #define HP_TIMING_PRINT(Buf, Len, Val) \
69   do {                                                                        \
70     char __buf[20];                                                           \
71     char *__cp = _itoa (Val, __buf + sizeof (__buf), 10, 0);                  \
72     int __len = (Len);                                                        \
73     char *__dest = (Buf);                                                     \
74     while (__len-- > 0 && __cp < __buf + sizeof (__buf))                      \
75       *__dest++ = *__cp++;                                                    \
76     memcpy (__dest, " clock cycles", MIN (__len, sizeof (" clock cycles")));  \
77   } while (0)
78 #endif  /* __i386__ */
79
80 rpmsw rpmswNow(rpmsw sw)
81 {
82 /*@-noeffect@*/
83     if (!rpmsw_initialized)
84         (void) rpmswInit();
85 /*@=noeffect@*/
86     if (sw == NULL)
87         return NULL;
88     switch (rpmsw_type) {
89     case 0:
90         if (gettimeofday(&sw->u.tv, NULL))
91             return NULL;
92         break;
93 #if defined(HP_TIMING_NOW)
94     case 1:
95         HP_TIMING_NOW(sw->u.ticks);
96         break;
97 #endif
98     }
99     return sw;
100 }
101
102 /** \ingroup rpmio
103  * Return difference of 2 timeval stamps in micro-seconds.
104  * @param *etv          end timeval
105  * @param *btv          begin timeval
106  * @return              difference in milli-seconds
107  */
108 static inline
109 rpmtime_t tvsub(/*@null@*/ const struct timeval * etv,
110                 /*@null@*/ const struct timeval * btv)
111         /*@*/
112 {
113     time_t secs, usecs;
114     if (etv == NULL  || btv == NULL) return 0;
115     secs = etv->tv_sec - btv->tv_sec;
116     for (usecs = etv->tv_usec - btv->tv_usec; usecs < 0; usecs += 1000000)
117         secs--;
118     return ((secs * 1000000) + usecs);
119 }
120
121 rpmtime_t rpmswDiff(rpmsw end, rpmsw begin)
122 {
123     unsigned long long ticks = 0;
124
125     if (end == NULL || begin == NULL)
126         return 0;
127     switch (rpmsw_type) {
128     default:
129     case 0:
130         ticks = tvsub(&end->u.tv, &begin->u.tv);
131         break;
132 #if defined(HP_TIMING_NOW)
133     case 1:
134         if (end->u.ticks > begin->u.ticks)
135             HP_TIMING_DIFF(ticks, begin->u.ticks, end->u.ticks);
136         break;
137 #endif
138     }
139     if (ticks >= rpmsw_overhead)
140         ticks -= rpmsw_overhead;
141     if (rpmsw_cycles > 1)
142         ticks /= rpmsw_cycles;
143     return ticks;
144 }
145
146 #if defined(HP_TIMING_NOW)
147 /*@-type@*/
148 static rpmtime_t rpmswCalibrate(void)
149         /*@globals internalState @*/
150         /*@modifies internalState @*/
151 {
152     struct rpmsw_s begin, end;
153     rpmtime_t ticks;
154     struct timespec req, rem;
155     int rc;
156     int i;
157
158 /*@-uniondef@*/
159     (void) rpmswNow(&begin);
160 /*@=uniondef@*/
161     req.tv_sec = 0;
162     req.tv_nsec = 20 * 1000 * 1000;
163     for (i = 0; i < 100; i++) {
164 /*@-compdef@*/
165         rc = nanosleep(&req, &rem);
166 /*@=compdef@*/
167         if (rc == 0)
168             break;
169         if (rem.tv_sec == 0 && rem.tv_nsec == 0)
170             break;
171         req = rem;      /* structure assignment */
172     }
173 /*@-uniondef@*/
174     ticks = rpmswDiff(rpmswNow(&end), &begin);
175 /*@=uniondef@*/
176
177     return ticks;
178 }
179 /*@=type@*/
180 #endif
181
182 rpmtime_t rpmswInit(void)
183         /*@globals rpmsw_cycles, rpmsw_initialized, rpmsw_overhead,
184                 rpmsw_type @*/
185         /*@modifies rpmsw_cycles, rpmsw_initialized, rpmsw_overhead,
186                 rpmsw_type @*/
187 {
188     struct rpmsw_s begin, end;
189     unsigned long long sum_cycles = 0;
190     rpmtime_t sum_usecs = 0;
191     rpmtime_t sum_overhead = 0;
192     rpmtime_t cycles;
193     int i;
194
195     rpmsw_initialized = 1;
196
197     rpmsw_overhead = 0;
198     rpmsw_cycles = 0;
199
200     /* Convergence for simultaneous cycles and overhead is overkill ... */
201     for (i = 0; i < 3; i++) {
202 #if defined(HP_TIMING_NOW)
203         rpmtime_t save_cycles = rpmsw_cycles;
204
205         /* We want cycles, not cycles/usec, here. */
206         rpmsw_cycles = 1;
207
208         /* Start wall clock. */
209         rpmsw_type = 0;
210 /*@-uniondef@*/
211         (void) rpmswNow(&begin);
212 /*@=uniondef@*/
213
214         /* Get no. of cycles while doing nanosleep. */
215         rpmsw_type = 1;
216         cycles = rpmswCalibrate();
217         if (save_cycles > 0 && rpmsw_overhead > 0)
218             cycles -= (save_cycles * rpmsw_overhead);
219         sum_cycles += cycles;
220
221         /* Compute wall clock delta in usecs. */
222         rpmsw_type = 0;
223 /*@-uniondef@*/
224         sum_usecs += rpmswDiff(rpmswNow(&end), &begin);
225 /*@=uniondef@*/
226         rpmsw_type = 1;
227
228         /* Compute cycles/usec */
229         rpmsw_cycles = sum_cycles/sum_usecs;
230 #else
231         rpmsw_type = 0;
232 #endif
233
234         /* Calculate timing overhead in usecs. */
235 /*@-uniondef@*/
236         (void) rpmswNow(&begin);
237         sum_overhead += rpmswDiff(rpmswNow(&end), &begin);
238 /*@=uniondef@*/
239
240         rpmsw_overhead = sum_overhead/(i+1);
241
242     }
243
244     return rpmsw_overhead;
245 }
246
247 /*@-mods@*/
248 int rpmswEnter(rpmop op, ssize_t rc)
249 {
250     if (op == NULL)
251         return 0;
252
253     op->count++;
254     if (rc < 0) {
255         op->bytes = 0;
256         op->usecs = 0;
257     }
258 /*@-uniondef@*/
259     (void) rpmswNow(&op->begin);
260 /*@=uniondef@*/
261     return 0;
262 }
263
264 rpmtime_t rpmswExit(rpmop op, ssize_t rc)
265 {
266     struct rpmsw_s end;
267
268     if (op == NULL)
269         return 0;
270
271 /*@-uniondef@*/
272     op->usecs += rpmswDiff(rpmswNow(&end), &op->begin);
273 /*@=uniondef@*/
274     if (rc > 0)
275         op->bytes += rc;
276     op->begin = end;    /* structure assignment */
277     return op->usecs;
278 }
279
280 rpmtime_t rpmswAdd(rpmop to, rpmop from)
281 {
282     rpmtime_t usecs = 0;
283     if (to != NULL && from != NULL) {
284         to->count += from->count;
285         to->bytes += from->bytes;
286         to->usecs += from->usecs;
287         usecs = to->usecs;
288     }
289     return usecs;
290 }
291
292 rpmtime_t rpmswSub(rpmop to, rpmop from)
293 {
294     rpmtime_t usecs = 0;
295     if (to != NULL && from != NULL) {
296         to->count -= from->count;
297         to->bytes -= from->bytes;
298         to->usecs -= from->usecs;
299         usecs = to->usecs;
300     }
301     return usecs;
302 }
303
304 /*@=mods@*/