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