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