1 /* Copyright (c) 2008, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23 /**
24 @file storage/perfschema/pfs_timer.cc
25 Performance schema timers (implementation).
26 */
27
28 #include "my_global.h"
29 #include "pfs_timer.h"
30 #include "my_rdtsc.h"
31
32 enum_timer_name idle_timer= TIMER_NAME_MICROSEC;
33 enum_timer_name wait_timer= TIMER_NAME_CYCLE;
34 enum_timer_name stage_timer= TIMER_NAME_NANOSEC;
35 enum_timer_name statement_timer= TIMER_NAME_NANOSEC;
36 enum_timer_name transaction_timer= TIMER_NAME_NANOSEC;
37 MY_TIMER_INFO pfs_timer_info;
38
39 static ulonglong cycle_v0;
40 static ulonglong nanosec_v0;
41 static ulonglong microsec_v0;
42 static ulonglong millisec_v0;
43 static ulonglong tick_v0;
44
45 static ulong cycle_to_pico; /* 1000 at 1 GHz, 333 at 3GHz, 250 at 4GHz */
46 static ulong nanosec_to_pico; /* In theory, 1 000 */
47 static ulong microsec_to_pico; /* In theory, 1 000 000 */
48 static ulong millisec_to_pico; /* In theory, 1 000 000 000, fits in uint32 */
49 static ulonglong tick_to_pico; /* 1e10 at 100 Hz, 1.666e10 at 60 Hz */
50
51 /* Indexed by enum enum_timer_name */
52 static struct time_normalizer to_pico_data[FIRST_TIMER_NAME + COUNT_TIMER_NAME]=
53 {
54 { 0, 0}, /* unused */
55 { 0, 0}, /* cycle */
56 { 0, 0}, /* nanosec */
57 { 0, 0}, /* microsec */
58 { 0, 0}, /* millisec */
59 { 0, 0} /* tick */
60 };
61
round_to_ulong(double value)62 static inline ulong round_to_ulong(double value)
63 {
64 return (ulong) (value + 0.5);
65 }
66
round_to_ulonglong(double value)67 static inline ulonglong round_to_ulonglong(double value)
68 {
69 return (ulonglong) (value + 0.5);
70 }
71
init_timers(void)72 void init_timers(void)
73 {
74 double pico_frequency= 1.0e12;
75
76 my_timer_init(&pfs_timer_info);
77
78 cycle_v0= my_timer_cycles();
79 nanosec_v0= my_timer_nanoseconds();
80 microsec_v0= my_timer_microseconds();
81 millisec_v0= my_timer_milliseconds();
82 tick_v0= my_timer_ticks();
83
84 if (pfs_timer_info.cycles.frequency > 0)
85 cycle_to_pico= round_to_ulong(pico_frequency/
86 (double)pfs_timer_info.cycles.frequency);
87 else
88 cycle_to_pico= 0;
89
90 if (pfs_timer_info.nanoseconds.frequency > 0)
91 nanosec_to_pico= round_to_ulong(pico_frequency/
92 (double)pfs_timer_info.nanoseconds.frequency);
93 else
94 nanosec_to_pico= 0;
95
96 if (pfs_timer_info.microseconds.frequency > 0)
97 microsec_to_pico= round_to_ulong(pico_frequency/
98 (double)pfs_timer_info.microseconds.frequency);
99 else
100 microsec_to_pico= 0;
101
102 if (pfs_timer_info.milliseconds.frequency > 0)
103 millisec_to_pico= round_to_ulong(pico_frequency/
104 (double)pfs_timer_info.milliseconds.frequency);
105 else
106 millisec_to_pico= 0;
107
108 if (pfs_timer_info.ticks.frequency > 0)
109 tick_to_pico= round_to_ulonglong(pico_frequency/
110 (double)pfs_timer_info.ticks.frequency);
111 else
112 tick_to_pico= 0;
113
114 to_pico_data[TIMER_NAME_CYCLE].m_v0= cycle_v0;
115 to_pico_data[TIMER_NAME_CYCLE].m_factor= cycle_to_pico;
116
117 to_pico_data[TIMER_NAME_NANOSEC].m_v0= nanosec_v0;
118 to_pico_data[TIMER_NAME_NANOSEC].m_factor= nanosec_to_pico;
119
120 to_pico_data[TIMER_NAME_MICROSEC].m_v0= microsec_v0;
121 to_pico_data[TIMER_NAME_MICROSEC].m_factor= microsec_to_pico;
122
123 to_pico_data[TIMER_NAME_MILLISEC].m_v0= millisec_v0;
124 to_pico_data[TIMER_NAME_MILLISEC].m_factor= millisec_to_pico;
125
126 to_pico_data[TIMER_NAME_TICK].m_v0= tick_v0;
127 to_pico_data[TIMER_NAME_TICK].m_factor= tick_to_pico;
128
129 /*
130 Depending on the platform and build options,
131 some timers may not be available.
132 Pick best replacements.
133 */
134
135 /*
136 For WAIT, the cycle timer is used by default. However, it is not available
137 on all architectures. Fall back to the nanosecond timer in this case. It is
138 unlikely that neither cycle nor nanosecond are available, but we continue
139 probing less resolution timers anyway for consistency with other events.
140 */
141
142 if (cycle_to_pico != 0)
143 {
144 /* Normal case. */
145 wait_timer= TIMER_NAME_CYCLE;
146 }
147 else if (nanosec_to_pico != 0)
148 {
149 /* Robustness, no known cases. */
150 wait_timer= TIMER_NAME_NANOSEC;
151 }
152 else if (microsec_to_pico != 0)
153 {
154 /* Robustness, no known cases. */
155 wait_timer= TIMER_NAME_MICROSEC;
156 }
157 else if (millisec_to_pico != 0)
158 {
159 /* Robustness, no known cases. */
160 wait_timer= TIMER_NAME_MILLISEC;
161 }
162 else
163 {
164 /*
165 Will never be reached on any architecture, but must provide a default if
166 no other timers are available.
167 */
168 wait_timer= TIMER_NAME_TICK;
169 }
170
171 /*
172 For STAGE and STATEMENT, a timer with a fixed frequency is better.
173 The prefered timer is nanosecond, or lower resolutions.
174 */
175
176 if (nanosec_to_pico != 0)
177 {
178 /* Normal case. */
179 stage_timer= TIMER_NAME_NANOSEC;
180 statement_timer= TIMER_NAME_NANOSEC;
181 transaction_timer= TIMER_NAME_NANOSEC;
182 }
183 else if (microsec_to_pico != 0)
184 {
185 /* Windows. */
186 stage_timer= TIMER_NAME_MICROSEC;
187 statement_timer= TIMER_NAME_MICROSEC;
188 transaction_timer= TIMER_NAME_MICROSEC;
189 }
190 else if (millisec_to_pico != 0)
191 {
192 /* Robustness, no known cases. */
193 stage_timer= TIMER_NAME_MILLISEC;
194 statement_timer= TIMER_NAME_MILLISEC;
195 transaction_timer= TIMER_NAME_MILLISEC;
196 }
197 else if (tick_to_pico != 0)
198 {
199 /* Robustness, no known cases. */
200 stage_timer= TIMER_NAME_TICK;
201 statement_timer= TIMER_NAME_TICK;
202 transaction_timer= TIMER_NAME_TICK;
203 }
204 else
205 {
206 /* Robustness, no known cases. */
207 stage_timer= TIMER_NAME_CYCLE;
208 statement_timer= TIMER_NAME_CYCLE;
209 transaction_timer= TIMER_NAME_CYCLE;
210 }
211
212 /*
213 For IDLE, a timer with a fixed frequency is critical,
214 as the CPU clock may slow down a lot if the server is completely idle.
215 The prefered timer is microsecond, or lower resolutions.
216 */
217
218 if (microsec_to_pico != 0)
219 {
220 /* Normal case. */
221 idle_timer= TIMER_NAME_MICROSEC;
222 }
223 else if (millisec_to_pico != 0)
224 {
225 /* Robustness, no known cases. */
226 wait_timer= TIMER_NAME_MILLISEC;
227 }
228 else if (tick_to_pico != 0)
229 {
230 /* Robustness, no known cases. */
231 idle_timer= TIMER_NAME_TICK;
232 }
233 else
234 {
235 /* Robustness, no known cases. */
236 idle_timer= TIMER_NAME_CYCLE;
237 }
238 }
239
get_timer_raw_value(enum_timer_name timer_name)240 ulonglong get_timer_raw_value(enum_timer_name timer_name)
241 {
242 switch (timer_name)
243 {
244 case TIMER_NAME_CYCLE:
245 return my_timer_cycles();
246 case TIMER_NAME_NANOSEC:
247 return my_timer_nanoseconds();
248 case TIMER_NAME_MICROSEC:
249 return my_timer_microseconds();
250 case TIMER_NAME_MILLISEC:
251 return my_timer_milliseconds();
252 case TIMER_NAME_TICK:
253 return my_timer_ticks();
254 default:
255 assert(false);
256 }
257 return 0;
258 }
259
get_timer_raw_value_and_function(enum_timer_name timer_name,timer_fct_t * fct)260 ulonglong get_timer_raw_value_and_function(enum_timer_name timer_name, timer_fct_t *fct)
261 {
262 switch (timer_name)
263 {
264 case TIMER_NAME_CYCLE:
265 *fct= my_timer_cycles;
266 return my_timer_cycles();
267 case TIMER_NAME_NANOSEC:
268 *fct= my_timer_nanoseconds;
269 return my_timer_nanoseconds();
270 case TIMER_NAME_MICROSEC:
271 *fct= my_timer_microseconds;
272 return my_timer_microseconds();
273 case TIMER_NAME_MILLISEC:
274 *fct= my_timer_milliseconds;
275 return my_timer_milliseconds();
276 case TIMER_NAME_TICK:
277 *fct= my_timer_ticks;
278 return my_timer_ticks();
279 default:
280 *fct= NULL;
281 assert(false);
282 }
283 return 0;
284 }
285
get_timer_pico_value(enum_timer_name timer_name)286 ulonglong get_timer_pico_value(enum_timer_name timer_name)
287 {
288 ulonglong result;
289
290 switch (timer_name)
291 {
292 case TIMER_NAME_CYCLE:
293 result= (my_timer_cycles() - cycle_v0) * cycle_to_pico;
294 break;
295 case TIMER_NAME_NANOSEC:
296 result= (my_timer_nanoseconds() - nanosec_v0) * nanosec_to_pico;
297 break;
298 case TIMER_NAME_MICROSEC:
299 result= (my_timer_microseconds() - microsec_v0) * microsec_to_pico;
300 break;
301 case TIMER_NAME_MILLISEC:
302 result= (my_timer_milliseconds() - millisec_v0) * millisec_to_pico;
303 break;
304 case TIMER_NAME_TICK:
305 result= (my_timer_ticks() - tick_v0) * tick_to_pico;
306 break;
307 default:
308 result= 0;
309 assert(false);
310 }
311 return result;
312 }
313
get(enum_timer_name timer_name)314 time_normalizer* time_normalizer::get(enum_timer_name timer_name)
315 {
316 uint index= static_cast<uint> (timer_name);
317
318 assert(index >= FIRST_TIMER_NAME);
319 assert(index <= LAST_TIMER_NAME);
320
321 return & to_pico_data[index];
322 }
323
to_pico(ulonglong start,ulonglong end,ulonglong * pico_start,ulonglong * pico_end,ulonglong * pico_wait)324 void time_normalizer::to_pico(ulonglong start, ulonglong end,
325 ulonglong *pico_start, ulonglong *pico_end, ulonglong *pico_wait)
326 {
327 if (start == 0)
328 {
329 *pico_start= 0;
330 *pico_end= 0;
331 *pico_wait= 0;
332 }
333 else
334 {
335 *pico_start= (start - m_v0) * m_factor;
336 if (end == 0)
337 {
338 *pico_end= 0;
339 *pico_wait= 0;
340 }
341 else
342 {
343 *pico_end= (end - m_v0) * m_factor;
344 *pico_wait= (end - start) * m_factor;
345 }
346 }
347 }
348
349