1 /* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
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 MY_TIMER_INFO pfs_timer_info;
37
38 static ulonglong cycle_v0;
39 static ulonglong nanosec_v0;
40 static ulonglong microsec_v0;
41 static ulonglong millisec_v0;
42 static ulonglong tick_v0;
43
44 static ulong cycle_to_pico; /* 1000 at 1 GHz, 333 at 3GHz, 250 at 4GHz */
45 static ulong nanosec_to_pico; /* In theory, 1 000 */
46 static ulong microsec_to_pico; /* In theory, 1 000 000 */
47 static ulong millisec_to_pico; /* In theory, 1 000 000 000, fits in uint32 */
48 static ulonglong tick_to_pico; /* 1e10 at 100 Hz, 1.666e10 at 60 Hz */
49
50 /* Indexed by enum enum_timer_name */
51 static struct time_normalizer to_pico_data[FIRST_TIMER_NAME + COUNT_TIMER_NAME]=
52 {
53 { 0, 0}, /* unused */
54 { 0, 0}, /* cycle */
55 { 0, 0}, /* nanosec */
56 { 0, 0}, /* microsec */
57 { 0, 0}, /* millisec */
58 { 0, 0} /* tick */
59 };
60
round_to_ulong(double value)61 static inline ulong round_to_ulong(double value)
62 {
63 return (ulong) (value + 0.5);
64 }
65
round_to_ulonglong(double value)66 static inline ulonglong round_to_ulonglong(double value)
67 {
68 return (ulonglong) (value + 0.5);
69 }
70
init_timers(void)71 void init_timers(void)
72 {
73 double pico_frequency= 1.0e12;
74
75 my_timer_init(&pfs_timer_info);
76
77 cycle_v0= my_timer_cycles();
78 nanosec_v0= my_timer_nanoseconds();
79 microsec_v0= my_timer_microseconds();
80 millisec_v0= my_timer_milliseconds();
81 tick_v0= my_timer_ticks();
82
83 if (pfs_timer_info.cycles.frequency > 0)
84 cycle_to_pico= round_to_ulong(pico_frequency/
85 (double)pfs_timer_info.cycles.frequency);
86 else
87 cycle_to_pico= 0;
88
89 if (pfs_timer_info.nanoseconds.frequency > 0)
90 nanosec_to_pico= round_to_ulong(pico_frequency/
91 (double)pfs_timer_info.nanoseconds.frequency);
92 else
93 nanosec_to_pico= 0;
94
95 if (pfs_timer_info.microseconds.frequency > 0)
96 microsec_to_pico= round_to_ulong(pico_frequency/
97 (double)pfs_timer_info.microseconds.frequency);
98 else
99 microsec_to_pico= 0;
100
101 if (pfs_timer_info.milliseconds.frequency > 0)
102 millisec_to_pico= round_to_ulong(pico_frequency/
103 (double)pfs_timer_info.milliseconds.frequency);
104 else
105 millisec_to_pico= 0;
106
107 if (pfs_timer_info.ticks.frequency > 0)
108 tick_to_pico= round_to_ulonglong(pico_frequency/
109 (double)pfs_timer_info.ticks.frequency);
110 else
111 tick_to_pico= 0;
112
113 to_pico_data[TIMER_NAME_CYCLE].m_v0= cycle_v0;
114 to_pico_data[TIMER_NAME_CYCLE].m_factor= cycle_to_pico;
115
116 to_pico_data[TIMER_NAME_NANOSEC].m_v0= nanosec_v0;
117 to_pico_data[TIMER_NAME_NANOSEC].m_factor= nanosec_to_pico;
118
119 to_pico_data[TIMER_NAME_MICROSEC].m_v0= microsec_v0;
120 to_pico_data[TIMER_NAME_MICROSEC].m_factor= microsec_to_pico;
121
122 to_pico_data[TIMER_NAME_MILLISEC].m_v0= millisec_v0;
123 to_pico_data[TIMER_NAME_MILLISEC].m_factor= millisec_to_pico;
124
125 to_pico_data[TIMER_NAME_TICK].m_v0= tick_v0;
126 to_pico_data[TIMER_NAME_TICK].m_factor= tick_to_pico;
127
128 /*
129 Depending on the platform and build options,
130 some timers may not be available.
131 Pick best replacements.
132 */
133
134 /*
135 For WAIT, the cycle timer is used by default. However, it is not available
136 on all architectures. Fall back to the nanosecond timer in this case. It is
137 unlikely that neither cycle nor nanosecond are available, but we continue
138 probing less resolution timers anyway for consistency with other events.
139 */
140
141 if (cycle_to_pico != 0)
142 {
143 /* Normal case. */
144 wait_timer= TIMER_NAME_CYCLE;
145 }
146 else if (nanosec_to_pico != 0)
147 {
148 /* Robustness, no known cases. */
149 wait_timer= TIMER_NAME_NANOSEC;
150 }
151 else if (microsec_to_pico != 0)
152 {
153 /* Robustness, no known cases. */
154 wait_timer= TIMER_NAME_MICROSEC;
155 }
156 else if (millisec_to_pico != 0)
157 {
158 /* Robustness, no known cases. */
159 wait_timer= TIMER_NAME_MILLISEC;
160 }
161 else
162 {
163 /*
164 Will never be reached on any architecture, but must provide a default if
165 no other timers are available.
166 */
167 wait_timer= TIMER_NAME_TICK;
168 }
169
170 /*
171 For STAGE and STATEMENT, a timer with a fixed frequency is better.
172 The prefered timer is nanosecond, or lower resolutions.
173 */
174
175 if (nanosec_to_pico != 0)
176 {
177 /* Normal case. */
178 stage_timer= TIMER_NAME_NANOSEC;
179 statement_timer= TIMER_NAME_NANOSEC;
180 }
181 else if (microsec_to_pico != 0)
182 {
183 /* Windows. */
184 stage_timer= TIMER_NAME_MICROSEC;
185 statement_timer= TIMER_NAME_MICROSEC;
186 }
187 else if (millisec_to_pico != 0)
188 {
189 /* Robustness, no known cases. */
190 stage_timer= TIMER_NAME_MILLISEC;
191 statement_timer= TIMER_NAME_MILLISEC;
192 }
193 else if (tick_to_pico != 0)
194 {
195 /* Robustness, no known cases. */
196 stage_timer= TIMER_NAME_TICK;
197 statement_timer= TIMER_NAME_TICK;
198 }
199 else
200 {
201 /* Robustness, no known cases. */
202 stage_timer= TIMER_NAME_CYCLE;
203 statement_timer= TIMER_NAME_CYCLE;
204 }
205
206 /*
207 For IDLE, a timer with a fixed frequency is critical,
208 as the CPU clock may slow down a lot if the server is completely idle.
209 The prefered timer is microsecond, or lower resolutions.
210 */
211
212 if (microsec_to_pico != 0)
213 {
214 /* Normal case. */
215 idle_timer= TIMER_NAME_MICROSEC;
216 }
217 else if (millisec_to_pico != 0)
218 {
219 /* Robustness, no known cases. */
220 wait_timer= TIMER_NAME_MILLISEC;
221 }
222 else if (tick_to_pico != 0)
223 {
224 /* Robustness, no known cases. */
225 idle_timer= TIMER_NAME_TICK;
226 }
227 else
228 {
229 /* Robustness, no known cases. */
230 idle_timer= TIMER_NAME_CYCLE;
231 }
232 }
233
get_timer_raw_value(enum_timer_name timer_name)234 ulonglong get_timer_raw_value(enum_timer_name timer_name)
235 {
236 switch (timer_name)
237 {
238 case TIMER_NAME_CYCLE:
239 return my_timer_cycles();
240 case TIMER_NAME_NANOSEC:
241 return my_timer_nanoseconds();
242 case TIMER_NAME_MICROSEC:
243 return my_timer_microseconds();
244 case TIMER_NAME_MILLISEC:
245 return my_timer_milliseconds();
246 case TIMER_NAME_TICK:
247 return my_timer_ticks();
248 default:
249 DBUG_ASSERT(false);
250 }
251 return 0;
252 }
253
get_timer_raw_value_and_function(enum_timer_name timer_name,timer_fct_t * fct)254 ulonglong get_timer_raw_value_and_function(enum_timer_name timer_name, timer_fct_t *fct)
255 {
256 switch (timer_name)
257 {
258 case TIMER_NAME_CYCLE:
259 *fct= my_timer_cycles;
260 return my_timer_cycles();
261 case TIMER_NAME_NANOSEC:
262 *fct= my_timer_nanoseconds;
263 return my_timer_nanoseconds();
264 case TIMER_NAME_MICROSEC:
265 *fct= my_timer_microseconds;
266 return my_timer_microseconds();
267 case TIMER_NAME_MILLISEC:
268 *fct= my_timer_milliseconds;
269 return my_timer_milliseconds();
270 case TIMER_NAME_TICK:
271 *fct= my_timer_ticks;
272 return my_timer_ticks();
273 default:
274 *fct= NULL;
275 DBUG_ASSERT(false);
276 }
277 return 0;
278 }
279
get_timer_pico_value(enum_timer_name timer_name)280 ulonglong get_timer_pico_value(enum_timer_name timer_name)
281 {
282 ulonglong result;
283
284 switch (timer_name)
285 {
286 case TIMER_NAME_CYCLE:
287 result= (my_timer_cycles() - cycle_v0) * cycle_to_pico;
288 break;
289 case TIMER_NAME_NANOSEC:
290 result= (my_timer_nanoseconds() - nanosec_v0) * nanosec_to_pico;
291 break;
292 case TIMER_NAME_MICROSEC:
293 result= (my_timer_microseconds() - microsec_v0) * microsec_to_pico;
294 break;
295 case TIMER_NAME_MILLISEC:
296 result= (my_timer_milliseconds() - millisec_v0) * millisec_to_pico;
297 break;
298 case TIMER_NAME_TICK:
299 result= (my_timer_ticks() - tick_v0) * tick_to_pico;
300 break;
301 default:
302 result= 0;
303 DBUG_ASSERT(false);
304 }
305 return result;
306 }
307
get(enum_timer_name timer_name)308 time_normalizer* time_normalizer::get(enum_timer_name timer_name)
309 {
310 uint index= static_cast<uint> (timer_name);
311
312 DBUG_ASSERT(index >= FIRST_TIMER_NAME);
313 DBUG_ASSERT(index <= LAST_TIMER_NAME);
314
315 return & to_pico_data[index];
316 }
317
to_pico(ulonglong start,ulonglong end,ulonglong * pico_start,ulonglong * pico_end,ulonglong * pico_wait)318 void time_normalizer::to_pico(ulonglong start, ulonglong end,
319 ulonglong *pico_start, ulonglong *pico_end, ulonglong *pico_wait)
320 {
321 if (start == 0)
322 {
323 *pico_start= 0;
324 *pico_end= 0;
325 *pico_wait= 0;
326 }
327 else
328 {
329 *pico_start= (start - m_v0) * m_factor;
330 if (end == 0)
331 {
332 *pico_end= 0;
333 *pico_wait= 0;
334 }
335 else
336 {
337 *pico_end= (end - m_v0) * m_factor;
338 *pico_wait= (end - start) * m_factor;
339 }
340 }
341 }
342
343