1 /* Copyright (C) 2014 Percona and Sergey Vojtovich
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
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,  USA */
15 
16 #include "mysql_version.h"
17 #include "my_global.h"
18 #include "mysql_com.h"
19 #include "rpl_tblmap.h"
20 #include "table.h"
21 #include "field.h"
22 #include "sql_show.h"
23 #include "query_response_time.h"
24 
25 #define TIME_STRING_POSITIVE_POWER_LENGTH QRT_TIME_STRING_POSITIVE_POWER_LENGTH
26 #define TIME_STRING_NEGATIVE_POWER_LENGTH 6
27 #define TOTAL_STRING_POSITIVE_POWER_LENGTH QRT_TOTAL_STRING_POSITIVE_POWER_LENGTH
28 #define TOTAL_STRING_NEGATIVE_POWER_LENGTH 6
29 #define MINIMUM_BASE 2
30 #define MAXIMUM_BASE QRT_MAXIMUM_BASE
31 #define POSITIVE_POWER_FILLER QRT_POSITIVE_POWER_FILLER
32 #define NEGATIVE_POWER_FILLER QRT_NEGATIVE_POWER_FILLER
33 #define TIME_OVERFLOW   QRT_TIME_OVERFLOW
34 #define DEFAULT_BASE    QRT_DEFAULT_BASE
35 
36 #define do_xstr(s) do_str(s)
37 #define do_str(s) #s
38 #define do_format(filler,width) "%" filler width "lld"
39 /*
40   Format strings for snprintf. Generate from:
41   POSITIVE_POWER_FILLER and TIME_STRING_POSITIVE_POWER_LENGTH
42   NEFATIVE_POWER_FILLER and TIME_STRING_NEGATIVE_POWER_LENGTH
43 */
44 #define TIME_STRING_POSITIVE_POWER_FORMAT do_format(POSITIVE_POWER_FILLER,do_xstr(TIME_STRING_POSITIVE_POWER_LENGTH))
45 #define TIME_STRING_NEGATIVE_POWER_FORMAT do_format(NEGATIVE_POWER_FILLER,do_xstr(TIME_STRING_NEGATIVE_POWER_LENGTH))
46 #define TIME_STRING_FORMAT		      TIME_STRING_POSITIVE_POWER_FORMAT "." TIME_STRING_NEGATIVE_POWER_FORMAT
47 
48 #define TOTAL_STRING_POSITIVE_POWER_FORMAT do_format(POSITIVE_POWER_FILLER,do_xstr(TOTAL_STRING_POSITIVE_POWER_LENGTH))
49 #define TOTAL_STRING_NEGATIVE_POWER_FORMAT do_format(NEGATIVE_POWER_FILLER,do_xstr(TOTAL_STRING_NEGATIVE_POWER_LENGTH))
50 #define TOTAL_STRING_FORMAT		      TOTAL_STRING_POSITIVE_POWER_FORMAT "." TOTAL_STRING_NEGATIVE_POWER_FORMAT
51 
52 #define TIME_STRING_LENGTH	QRT_TIME_STRING_LENGTH
53 #define TIME_STRING_BUFFER_LENGTH	(TIME_STRING_LENGTH + 1 /* '\0' */)
54 
55 #define TOTAL_STRING_LENGTH	QRT_TOTAL_STRING_LENGTH
56 #define TOTAL_STRING_BUFFER_LENGTH	(TOTAL_STRING_LENGTH + 1 /* '\0' */)
57 
58 /*
59   Calculate length of "log linear"
60   1)
61   (MINIMUM_BASE ^ result) <= (10 ^ STRING_POWER_LENGTH) < (MINIMUM_BASE ^ (result + 1))
62 
63   2)
64   (MINIMUM_BASE ^ result) <= (10 ^ STRING_POWER_LENGTH)
65   and
66   (MINIMUM_BASE ^ (result + 1)) > (10 ^ STRING_POWER_LENGTH)
67 
68   3)
69   result     <= LOG(MINIMUM_BASE, 10 ^ STRING_POWER_LENGTH)= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10)
70   result + 1 >  LOG(MINIMUM_BASE, 10 ^ STRING_POWER_LENGTH)= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10)
71 
72   4) STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10) - 1 < result <= STRING_POWER_LENGTH * LOG(MINIMUM_BASE,10)
73 
74   MINIMUM_BASE= 2 always, LOG(MINIMUM_BASE,10)= 3.3219280948873626, result= (int)3.3219280948873626 * STRING_POWER_LENGTH
75 
76   Last counter always use for time overflow
77 */
78 #define POSITIVE_POWER_COUNT ((int)(332192809ULL * TIME_STRING_POSITIVE_POWER_LENGTH / 100000000ULL))
79 #define NEGATIVE_POWER_COUNT ((int)(332192809ULL * TIME_STRING_NEGATIVE_POWER_LENGTH / 100000000ULL))
80 #define OVERALL_POWER_COUNT (NEGATIVE_POWER_COUNT + 1 + POSITIVE_POWER_COUNT)
81 
82 #define MILLION ((unsigned long)1000 * 1000)
83 
84 namespace query_response_time
85 {
86 
87 class utility
88 {
89 public:
utility()90   utility() : m_base(0)
91   {
92     m_max_dec_value= MILLION;
93     for(int i= 0; TIME_STRING_POSITIVE_POWER_LENGTH > i; ++i)
94       m_max_dec_value *= 10;
95     setup(DEFAULT_BASE);
96   }
97 public:
base() const98   uint      base()            const { return m_base; }
negative_count() const99   uint      negative_count()  const { return m_negative_count; }
positive_count() const100   uint      positive_count()  const { return m_positive_count; }
bound_count() const101   uint      bound_count()     const { return m_bound_count; }
max_dec_value() const102   ulonglong max_dec_value()   const { return m_max_dec_value; }
bound(uint index) const103   ulonglong bound(uint index) const { return m_bound[ index ]; }
104 public:
setup(uint base)105   void setup(uint base)
106   {
107     if(base != m_base)
108     {
109       m_base= base;
110 
111       const ulonglong million= 1000 * 1000;
112       ulonglong value= million;
113       m_negative_count= 0;
114       while(value > 0)
115       {
116 	m_negative_count += 1;
117 	value /= m_base;
118       }
119       m_negative_count -= 1;
120 
121       value= million;
122       m_positive_count= 0;
123       while(value < m_max_dec_value)
124       {
125 	m_positive_count += 1;
126 	value *= m_base;
127       }
128       m_bound_count= m_negative_count + m_positive_count;
129 
130       value= million;
131       for(uint i= 0; i < m_negative_count; ++i)
132       {
133 	value /= m_base;
134 	m_bound[m_negative_count - i - 1]= value;
135       }
136       value= million;
137       for(uint i= 0; i < m_positive_count;  ++i)
138       {
139 	m_bound[m_negative_count + i]= value;
140 	value *= m_base;
141       }
142     }
143   }
144 private:
145   uint      m_base;
146   uint      m_negative_count;
147   uint      m_positive_count;
148   uint      m_bound_count;
149   ulonglong m_max_dec_value; /* for TIME_STRING_POSITIVE_POWER_LENGTH=7 is 10000000 */
150   ulonglong m_bound[OVERALL_POWER_COUNT];
151 };
152 
153 static
print_time(char * buffer,std::size_t buffer_size,const char * format,uint64 value)154 void print_time(char* buffer, std::size_t buffer_size, const char* format,
155                 uint64 value)
156 {
157   ulonglong second=      (value / MILLION);
158   ulonglong microsecond= (value % MILLION);
159   my_snprintf(buffer, buffer_size, format, second, microsecond);
160 }
161 
162 class time_collector
163 {
164 public:
time_collector(utility & u)165   time_collector(utility& u) : m_utility(&u)
166   {
167   }
count(QUERY_TYPE type,uint index)168   uint32 count(QUERY_TYPE type, uint index)
169   {
170     return my_atomic_load32((int32*)&m_count[type][index]);
171   }
total(QUERY_TYPE type,uint index)172   uint64 total(QUERY_TYPE type, uint index)
173   {
174     return my_atomic_load64((int64*)&m_total[type][index]);
175   }
176 public:
flush()177   void flush()
178   {
179     memset((void*)&m_count,0,sizeof(m_count));
180     memset((void*)&m_total,0,sizeof(m_total));
181   }
collect(QUERY_TYPE type,uint64 time)182   void collect(QUERY_TYPE type, uint64 time)
183   {
184     int i= 0;
185     for(int count= m_utility->bound_count(); count > i; ++i)
186     {
187       if(m_utility->bound(i) > time)
188       {
189         my_atomic_add32((int32*)(&m_count[0][i]), 1);
190         my_atomic_add64((int64*)(&m_total[0][i]), time);
191         my_atomic_add32((int32*)(&m_count[type][i]), 1);
192         my_atomic_add64((int64*)(&m_total[type][i]), time);
193         break;
194       }
195     }
196   }
197 private:
198   utility* m_utility;
199   /*
200    The first row is for overall statistics,
201    the second row is for 'read' queries,
202    the third row is for 'write' queries.
203   */
204   uint32   m_count[3][OVERALL_POWER_COUNT + 1];
205   uint64   m_total[3][OVERALL_POWER_COUNT + 1];
206 };
207 
208 class collector
209 {
210 public:
collector()211   collector() : m_time(m_utility)
212   {
213     m_utility.setup(DEFAULT_BASE);
214     m_time.flush();
215   }
216 public:
flush()217   void flush()
218   {
219     m_utility.setup(opt_query_response_time_range_base);
220     m_time.flush();
221   }
fill(QUERY_TYPE type,THD * thd,TABLE_LIST * tables,COND * cond)222   int fill(QUERY_TYPE type,
223            THD* thd,
224            TABLE_LIST *tables, COND *cond)
225   {
226     DBUG_ENTER("fill_schema_query_response_time");
227     TABLE        *table= static_cast<TABLE*>(tables->table);
228     Field        **fields= table->field;
229     for(uint i= 0, count= bound_count() + 1 /* with overflow */; count > i; ++i)
230     {
231       char time[TIME_STRING_BUFFER_LENGTH];
232       char total[TOTAL_STRING_BUFFER_LENGTH];
233       if(i == bound_count())
234       {
235         assert(sizeof(TIME_OVERFLOW) <= TIME_STRING_BUFFER_LENGTH);
236         assert(sizeof(TIME_OVERFLOW) <= TOTAL_STRING_BUFFER_LENGTH);
237         memcpy(time,TIME_OVERFLOW,sizeof(TIME_OVERFLOW));
238         memcpy(total,TIME_OVERFLOW,sizeof(TIME_OVERFLOW));
239       }
240       else
241       {
242         print_time(time, sizeof(time), TIME_STRING_FORMAT, this->bound(i));
243         print_time(total, sizeof(total), TOTAL_STRING_FORMAT, this->total(type, i));
244       }
245       fields[0]->store(time,strlen(time),system_charset_info);
246       fields[1]->store(this->count(type, i));
247       fields[2]->store(total,strlen(total),system_charset_info);
248       if (schema_table_store_record(thd, table))
249       {
250 	DBUG_RETURN(1);
251       }
252     }
253     DBUG_RETURN(0);
254   }
collect(QUERY_TYPE type,ulonglong time)255   void collect(QUERY_TYPE type, ulonglong time)
256   {
257     m_time.collect(type, time);
258   }
bound_count() const259   uint bound_count() const
260   {
261     return m_utility.bound_count();
262   }
bound(uint index)263   ulonglong bound(uint index)
264   {
265     return m_utility.bound(index);
266   }
count(QUERY_TYPE type,uint index)267   ulonglong count(QUERY_TYPE type, uint index)
268   {
269     return m_time.count(type, index);
270   }
total(QUERY_TYPE type,uint index)271   ulonglong total(QUERY_TYPE type, uint index)
272   {
273     return m_time.total(type, index);
274   }
275 private:
276   utility          m_utility;
277   time_collector   m_time;
278 };
279 
280 static collector g_collector;
281 
282 } // namespace query_response_time
283 
query_response_time_init()284 void query_response_time_init()
285 {
286   query_response_time_flush();
287 }
288 
query_response_time_free()289 void query_response_time_free()
290 {
291   query_response_time::g_collector.flush();
292 }
293 
query_response_time_flush()294 void query_response_time_flush()
295 {
296   query_response_time::g_collector.flush();
297 }
298 
query_response_time_collect(QUERY_TYPE type,ulonglong query_time)299 void query_response_time_collect(QUERY_TYPE type,
300                                  ulonglong query_time)
301 {
302   query_response_time::g_collector.collect(type, query_time);
303 }
304 
query_response_time_fill(THD * thd,TABLE_LIST * tables,COND * cond)305 int query_response_time_fill(THD* thd, TABLE_LIST *tables, COND *cond)
306 {
307   return query_response_time::g_collector.fill(ANY, thd, tables, cond);
308 }
309 
query_response_time_fill_ro(THD * thd,TABLE_LIST * tables,COND * cond)310 int query_response_time_fill_ro(THD* thd, TABLE_LIST *tables, COND *cond)
311 {
312   return query_response_time::g_collector.fill(READ, thd, tables, cond);
313 }
314 
query_response_time_fill_rw(THD * thd,TABLE_LIST * tables,COND * cond)315 int query_response_time_fill_rw(THD* thd, TABLE_LIST *tables, COND *cond)
316 {
317   return query_response_time::g_collector.fill(WRITE, thd, tables, cond);
318 }
319