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, ×pec_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(×pec_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