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