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