xref: /trafficserver/src/tscore/ink_hrtime.cc (revision 4cfd5a73)
1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 /**************************************************************************
25 
26   ink_hrtime.cc
27 
28   This file contains code supporting the Inktomi high-resolution timer.
29 **************************************************************************/
30 
31 #include "tscore/ink_hrtime.h"
32 #include "tscore/ink_assert.h"
33 #include "tscore/ink_defs.h"
34 
35 #if defined(freebsd)
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <sys/sysctl.h>
39 #endif
40 #include <cstring>
41 #include <sys/time.h>
42 
43 char *
44 int64_to_str(char *buf, unsigned int buf_size, int64_t val, unsigned int *total_chars, unsigned int req_width, char pad_char)
45 {
46   const unsigned int local_buf_size = 32;
47   char local_buf[local_buf_size];
48   bool using_local_buffer = false;
49   bool negative           = false;
50   char *out_buf;
51 
52   if (buf_size < 22) {
53     // int64_t may not fit in provided buffer, use the local one
54     out_buf            = &local_buf[local_buf_size - 1];
55     using_local_buffer = true;
56   } else {
57     out_buf = &buf[buf_size - 1];
58   }
59 
60   unsigned int num_chars = 1; // includes eos
61   *out_buf--             = 0;
62 
63   if (val < 0) {
64     val      = -val;
65     negative = true;
66   }
67 
68   if (val < 10) {
69     *out_buf-- = '0' + static_cast<char>(val);
70     ++num_chars;
71   } else {
72     do {
73       *out_buf-- = static_cast<char>(val % 10) + '0';
74       val /= 10;
75       ++num_chars;
76     } while (val);
77   }
78 
79   // pad with pad_char if needed
80   //
81   if (req_width) {
82     // add minus sign if padding character is not 0
83     if (negative && pad_char != '0') {
84       *out_buf = '-';
85       ++num_chars;
86     } else {
87       out_buf++;
88     }
89     if (req_width > buf_size) {
90       req_width = buf_size;
91     }
92     unsigned int num_padding = 0;
93     if (req_width > num_chars) {
94       num_padding = req_width - num_chars;
95       switch (num_padding) {
96       case 3:
97         *--out_buf = pad_char;
98         // fallthrough
99 
100       case 2:
101         *--out_buf = pad_char;
102         // fallthrough
103 
104       case 1:
105         *--out_buf = pad_char;
106         break;
107 
108       default:
109         for (unsigned int i = 0; i < num_padding; ++i, *--out_buf = pad_char) {
110           ;
111         }
112       }
113       num_chars += num_padding;
114     }
115     // add minus sign if padding character is 0
116     if (negative && pad_char == '0') {
117       if (num_padding) {
118         *out_buf = '-'; // overwrite padding
119       } else {
120         *--out_buf = '-';
121         ++num_chars;
122       }
123     }
124   } else if (negative) {
125     *out_buf = '-';
126     ++num_chars;
127   } else {
128     out_buf++;
129   }
130 
131   if (using_local_buffer) {
132     if (num_chars <= buf_size) {
133       memcpy(buf, out_buf, num_chars);
134       out_buf = buf;
135     } else {
136       // data does not fit return nullptr
137       out_buf = nullptr;
138     }
139   }
140 
141   if (total_chars) {
142     *total_chars = num_chars;
143   }
144   return out_buf;
145 }
146 
147 int
148 squid_timestamp_to_buf(char *buf, unsigned int buf_size, long timestamp_sec, long timestamp_usec)
149 {
150   int res;
151   const unsigned int tmp_buf_size = 32;
152   char tmp_buf[tmp_buf_size];
153 
154   unsigned int num_chars_s;
155   char *ts_s = int64_to_str(tmp_buf, tmp_buf_size - 4, timestamp_sec, &num_chars_s, 0, '0');
156   ink_assert(ts_s);
157 
158   // convert milliseconds
159   //
160   tmp_buf[tmp_buf_size - 5] = '.';
161   int ms                    = timestamp_usec / 1000;
162   unsigned int num_chars_ms;
163   char ATS_UNUSED *ts_ms = int64_to_str(&tmp_buf[tmp_buf_size - 4], 4, ms, &num_chars_ms, 4, '0');
164   ink_assert(ts_ms && num_chars_ms == 4);
165 
166   unsigned int chars_to_write = num_chars_s + 3; // no eos
167 
168   if (buf_size >= chars_to_write) {
169     memcpy(buf, ts_s, chars_to_write);
170     res = chars_to_write;
171   } else {
172     res = -(static_cast<int>(chars_to_write));
173   }
174 
175   return res;
176 }
177 
178 #ifdef USE_TIME_STAMP_COUNTER_HRTIME
179 uint32_t
180 init_hrtime_TCS()
181 {
182   int freqlen = sizeof(hrtime_freq);
183   if (sysctlbyname("machdep.tsc_freq", &hrtime_freq, (size_t *)&freqlen, nullptr, 0) < 0) {
184     perror("sysctl: machdep.tsc_freq");
185     exit(1);
186   }
187   hrtime_freq_float = (double)1000000000 / (double)hrtime_freq;
188   return hrtime_freq;
189 }
190 
191 double hrtime_freq_float = 0.5; // 500 Mhz
192 uint32_t hrtime_freq     = init_hrtime_TCS();
193 #endif
194 
195 #ifdef NEED_HRTIME_BASIS
196 timespec timespec_basis;
197 ink_hrtime hrtime_offset;
198 ink_hrtime hrtime_basis = init_hrtime_basis();
199 
200 ink_hrtime
201 init_hrtime_basis()
202 {
203   ink_hrtime t1, t2, b, now;
204   timespec ts;
205 #ifdef USE_TIME_STAMP_COUNTER_HRTIME
206   init_hrtime_TCS();
207 #endif
208   do {
209     t1 = ink_get_hrtime_internal();
210 #if HAVE_CLOCK_GETTIME
211     ink_assert(!clock_gettime(CLOCK_REALTIME, &timespec_basis));
212 #else
213     {
214       struct timeval tnow;
215       ink_assert(!gettimeofday(&tnow, nullptr));
216       timespec_basis.tv_sec  = tnow.tv_sec;
217       timespec_basis.tv_nsec = tnow.tv_usec * 1000;
218     }
219 #endif
220     t2 = ink_get_hrtime_internal();
221     // accuracy must be at least 100 microseconds
222   } while (t2 - t1 > HRTIME_USECONDS(100));
223   b   = (t2 + t1) / 2;
224   now = ink_hrtime_from_timespec(&timespec_basis);
225   ts  = ink_hrtime_to_timespec(now);
226   ink_assert(ts.tv_sec == timespec_basis.tv_sec && ts.tv_nsec == timespec_basis.tv_nsec);
227   hrtime_offset = now - b;
228   hrtime_basis  = b;
229   return b;
230 }
231 #endif
232