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 #ifdef HAVE_SYS_SYSCTL_H
39 #include <sys/sysctl.h>
40 #endif
41 #endif
42 #include <cstring>
43 #include <sys/time.h>
44 
45 char *
int64_to_str(char * buf,unsigned int buf_size,int64_t val,unsigned int * total_chars,unsigned int req_width,char pad_char)46 int64_to_str(char *buf, unsigned int buf_size, int64_t val, unsigned int *total_chars, unsigned int req_width, char pad_char)
47 {
48   const unsigned int local_buf_size = 32;
49   char local_buf[local_buf_size];
50   bool using_local_buffer = false;
51   bool negative           = false;
52   char *out_buf           = buf;
53   char *working_buf;
54 
55   if (buf_size < 22) {
56     // int64_t may not fit in provided buffer, use the local one
57     working_buf        = &local_buf[local_buf_size - 1];
58     using_local_buffer = true;
59   } else {
60     working_buf = &buf[buf_size - 1];
61   }
62 
63   unsigned int num_chars = 1; // includes eos
64   *working_buf--         = 0;
65 
66   if (val < 0) {
67     val      = -val;
68     negative = true;
69   }
70 
71   if (val < 10) {
72     *working_buf-- = '0' + static_cast<char>(val);
73     ++num_chars;
74   } else {
75     do {
76       *working_buf-- = static_cast<char>(val % 10) + '0';
77       val /= 10;
78       ++num_chars;
79     } while (val);
80   }
81 
82   // pad with pad_char if needed
83   //
84   if (req_width) {
85     // add minus sign if padding character is not 0
86     if (negative && pad_char != '0') {
87       *working_buf = '-';
88       ++num_chars;
89     } else {
90       working_buf++;
91     }
92     if (req_width > buf_size) {
93       req_width = buf_size;
94     }
95     unsigned int num_padding = 0;
96     if (req_width > num_chars) {
97       num_padding = req_width - num_chars;
98       switch (num_padding) {
99       case 3:
100         *--working_buf = pad_char;
101         // fallthrough
102 
103       case 2:
104         *--working_buf = pad_char;
105         // fallthrough
106 
107       case 1:
108         *--working_buf = pad_char;
109         break;
110 
111       default:
112         for (unsigned int i = 0; i < num_padding; ++i, *--working_buf = pad_char) {
113           ;
114         }
115       }
116       num_chars += num_padding;
117     }
118     // add minus sign if padding character is 0
119     if (negative && pad_char == '0') {
120       if (num_padding) {
121         *working_buf = '-'; // overwrite padding
122       } else {
123         *--working_buf = '-';
124         ++num_chars;
125       }
126     }
127   } else if (negative) {
128     *working_buf = '-';
129     ++num_chars;
130   } else {
131     working_buf++;
132   }
133 
134   if (using_local_buffer) {
135     if (num_chars <= buf_size) {
136       memcpy(buf, working_buf, num_chars);
137       // out_buf is already pointing to buf
138     } else {
139       // data does not fit return nullptr
140       out_buf = nullptr;
141     }
142   }
143 
144   if (total_chars) {
145     *total_chars = num_chars;
146   }
147 
148   return out_buf;
149 }
150 
151 int
squid_timestamp_to_buf(char * buf,unsigned int buf_size,long timestamp_sec,long timestamp_usec)152 squid_timestamp_to_buf(char *buf, unsigned int buf_size, long timestamp_sec, long timestamp_usec)
153 {
154   int res;
155   const unsigned int tmp_buf_size = 32;
156   char tmp_buf[tmp_buf_size];
157 
158   unsigned int num_chars_s;
159   char *ts_s = int64_to_str(tmp_buf, tmp_buf_size - 4, timestamp_sec, &num_chars_s, 0, '0');
160   ink_assert(ts_s);
161 
162   // convert milliseconds
163   //
164   tmp_buf[tmp_buf_size - 5] = '.';
165   int ms                    = timestamp_usec / 1000;
166   unsigned int num_chars_ms;
167   char ATS_UNUSED *ts_ms = int64_to_str(&tmp_buf[tmp_buf_size - 4], 4, ms, &num_chars_ms, 4, '0');
168   ink_assert(ts_ms && num_chars_ms == 4);
169 
170   unsigned int chars_to_write = num_chars_s + 3; // no eos
171 
172   if (buf_size >= chars_to_write) {
173     memcpy(buf, ts_s, chars_to_write);
174     res = chars_to_write;
175   } else {
176     res = -(static_cast<int>(chars_to_write));
177   }
178 
179   return res;
180 }
181 
182 #ifdef USE_TIME_STAMP_COUNTER_HRTIME
183 uint32_t
init_hrtime_TCS()184 init_hrtime_TCS()
185 {
186   int freqlen = sizeof(hrtime_freq);
187   if (sysctlbyname("machdep.tsc_freq", &hrtime_freq, (size_t *)&freqlen, nullptr, 0) < 0) {
188     perror("sysctl: machdep.tsc_freq");
189     exit(1);
190   }
191   hrtime_freq_float = (double)1000000000 / (double)hrtime_freq;
192   return hrtime_freq;
193 }
194 
195 double hrtime_freq_float = 0.5; // 500 Mhz
196 uint32_t hrtime_freq     = init_hrtime_TCS();
197 #endif
198 
199 #ifdef NEED_HRTIME_BASIS
200 timespec timespec_basis;
201 ink_hrtime hrtime_offset;
202 ink_hrtime hrtime_basis = init_hrtime_basis();
203 
204 ink_hrtime
init_hrtime_basis()205 init_hrtime_basis()
206 {
207   ink_hrtime t1, t2, b, now;
208   timespec ts;
209 #ifdef USE_TIME_STAMP_COUNTER_HRTIME
210   init_hrtime_TCS();
211 #endif
212   do {
213     t1 = ink_get_hrtime_internal();
214 #if HAVE_CLOCK_GETTIME
215     ink_assert(!clock_gettime(CLOCK_REALTIME, &timespec_basis));
216 #else
217     {
218       struct timeval tnow;
219       ink_assert(!gettimeofday(&tnow, nullptr));
220       timespec_basis.tv_sec  = tnow.tv_sec;
221       timespec_basis.tv_nsec = tnow.tv_usec * 1000;
222     }
223 #endif
224     t2 = ink_get_hrtime_internal();
225     // accuracy must be at least 100 microseconds
226   } while (t2 - t1 > HRTIME_USECONDS(100));
227   b   = (t2 + t1) / 2;
228   now = ink_hrtime_from_timespec(&timespec_basis);
229   ts  = ink_hrtime_to_timespec(now);
230   ink_assert(ts.tv_sec == timespec_basis.tv_sec && ts.tv_nsec == timespec_basis.tv_nsec);
231   hrtime_offset = now - b;
232   hrtime_basis  = b;
233   return b;
234 }
235 #endif
236