1 /* Copyright (c) 2014 Percona LLC and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License
5 as published by the Free Software Foundation; version 2 of
6 the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
16
17 #include <mysql/plugin.h>
18 #include <mysql/plugin_audit.h>
19 #include <my_global.h>
20 #include <my_sys.h>
21 #include <my_list.h>
22 #include <my_pthread.h>
23 #include <typelib.h>
24 #include <limits.h>
25 #include <string.h>
26
27 static volatile ulonglong starttime= 0;
28 static volatile ulonglong concurrency= 0;
29 static volatile ulonglong busystart= 0;
30 static volatile ulonglong busytime= 0;
31 static volatile ulonglong totaltime= 0;
32 static volatile ulonglong queries= 0;
33
34 #ifdef HAVE_PSI_INTERFACE
35 PSI_mutex_key key_thd_list_mutex;
36 #endif
37 mysql_mutex_t thd_list_mutex;
38
39 LIST *thd_list_root= NULL;
40
41 typedef struct sm_thd_data_struct {
42 ulonglong start;
43 ulonglong duration;
44 ulonglong queries;
45 LIST *backref;
46 } sm_thd_data_t;
47
48 typedef enum { CTL_ON= 0, CTL_OFF= 1, CTL_RESET= 2 } sm_ctl_t;
49 static const char* sm_ctl_names[]= { "ON", "OFF", "RESET", NullS };
50 static TYPELIB sm_ctl_typelib= {
51 array_elements(sm_ctl_names) - 1,
52 "",
53 sm_ctl_names,
54 NULL
55 };
56
57 static
58 void sm_ctl_update(MYSQL_THD thd, struct st_mysql_sys_var *var,
59 void *var_ptr, const void *save);
60
61 static ulong sm_ctl= CTL_OFF;
62
63 static
64 MYSQL_THDVAR_ULONGLONG(thd_data,
65 PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT,
66 "scalability metrics data", NULL, NULL, 0, 0, ULONGLONG_MAX, 0);
67
68
69 static MYSQL_SYSVAR_ENUM(
70 control, /* name */
71 sm_ctl, /* var */
72 PLUGIN_VAR_RQCMDARG,
73 "Control the scalability metrics. Use this to turn ON/OFF or RESET metrics.",
74 NULL, /* check func. */
75 sm_ctl_update, /* update func. */
76 CTL_OFF, /* default */
77 &sm_ctl_typelib /* typelib */
78 );
79
80 static
sm_clock_time_get()81 ulonglong sm_clock_time_get()
82 {
83 #if (defined HAVE_CLOCK_GETTIME)
84 struct timespec ts;
85 #ifdef CLOCK_MONOTONIC_RAW
86 clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
87 #else
88 clock_gettime(CLOCK_MONOTONIC, &ts);
89 #endif
90 return((ulonglong) ts.tv_sec * 1000000000 + ts.tv_nsec);
91 #else
92 /* since output values measured in microseconds anyway,
93 100 nanoseconds precision should be enough here */
94 return(my_getsystime() * 100);
95 #endif
96 }
97
98
99 /* Get duration in microseconds */
100 static
sm_clock_time_duration(ulonglong beg,ulonglong end)101 ulonglong sm_clock_time_duration(ulonglong beg, ulonglong end)
102 {
103 return((end - beg) / 1000);
104 }
105
106 static
sm_thd_data_get(MYSQL_THD thd)107 sm_thd_data_t *sm_thd_data_get(MYSQL_THD thd)
108 {
109 sm_thd_data_t *thd_data = (sm_thd_data_t *) (intptr) THDVAR(thd, thd_data);
110 if (unlikely(thd_data == NULL))
111 {
112 thd_data= my_malloc(sizeof(sm_thd_data_t),
113 MYF(MY_FAE | MY_WME | MY_ZEROFILL));
114 mysql_mutex_lock(&thd_list_mutex);
115 thd_data->backref= list_push(thd_list_root, thd_data);
116 mysql_mutex_unlock(&thd_list_mutex);
117 THDVAR(thd, thd_data)= (ulonglong) (intptr) thd_data;
118 }
119 return thd_data;
120 }
121
122
123 static
sm_thd_data_release(MYSQL_THD thd)124 void sm_thd_data_release(MYSQL_THD thd)
125 {
126 sm_thd_data_t *thd_data = (sm_thd_data_t *) (intptr) THDVAR(thd, thd_data);
127 if (likely(thd_data != NULL && thd_data->backref != NULL))
128 {
129 (void) __sync_add_and_fetch(&queries, thd_data->queries);
130 (void) __sync_add_and_fetch(&totaltime, thd_data->duration);
131 mysql_mutex_lock(&thd_list_mutex);
132 thd_list_root= list_delete(thd_list_root, thd_data->backref);
133 mysql_mutex_unlock(&thd_list_mutex);
134 my_free(thd_data->backref);
135 my_free(thd_data);
136 THDVAR(thd, thd_data)= 0;
137 }
138 }
139
140 static
sm_reset_one(void * data,void * argument)141 int sm_reset_one(void *data, void *argument __attribute__((unused)))
142 {
143 sm_thd_data_t *thd_data= (sm_thd_data_t *) data;
144 thd_data->queries= 0;
145 thd_data->duration= 0;
146 return(0);
147 }
148
149 static
sm_reset()150 void sm_reset()
151 {
152 starttime= sm_clock_time_get();
153 busytime= totaltime= queries= 0;
154 mysql_mutex_lock(&thd_list_mutex);
155 list_walk(thd_list_root, sm_reset_one, NULL);
156 mysql_mutex_unlock(&thd_list_mutex);
157 }
158
159
160 static
sm_ctl_update(MYSQL_THD thd,struct st_mysql_sys_var * var,void * var_ptr,const void * save)161 void sm_ctl_update(MYSQL_THD thd __attribute__((unused)),
162 struct st_mysql_sys_var *var __attribute__((unused)),
163 void *var_ptr __attribute__((unused)),
164 const void *save) {
165 ulong new_val= *((sm_ctl_t*) save);
166
167 if (new_val != sm_ctl)
168 sm_reset();
169
170 if (new_val != CTL_RESET)
171 {
172 sm_ctl= new_val;
173
174 if (new_val == CTL_OFF)
175 {
176 mysql_mutex_lock(&thd_list_mutex);
177 list_free(thd_list_root, TRUE);
178 thd_list_root= NULL;
179 mysql_mutex_unlock(&thd_list_mutex);
180 }
181 }
182
183 }
184
185
186 static
sm_plugin_init(void * arg)187 int sm_plugin_init(void *arg __attribute__((unused)))
188 {
189 mysql_mutex_init(key_thd_list_mutex, &thd_list_mutex, MY_MUTEX_INIT_FAST);
190
191 sm_reset();
192
193 return(0);
194 }
195
196
197 static
sm_plugin_deinit(void * arg)198 int sm_plugin_deinit(void *arg __attribute__((unused)))
199 {
200 list_free(thd_list_root, TRUE);
201 thd_list_root= NULL;
202
203 mysql_mutex_destroy(&thd_list_mutex);
204
205 return(0);
206 }
207
208 static
sm_query_started(MYSQL_THD thd,const char * query)209 void sm_query_started(MYSQL_THD thd,
210 const char* query __attribute__((unused))) {
211
212 sm_thd_data_t *thd_data= sm_thd_data_get(thd);
213
214 if (__sync_bool_compare_and_swap(&concurrency, 0, 1))
215 {
216 thd_data->start= sm_clock_time_get();
217 busystart= thd_data->start;
218 }
219 else
220 {
221 thd_data->start= sm_clock_time_get();
222 (void) __sync_add_and_fetch(&concurrency, 1);
223 }
224 }
225
226 static
sm_query_finished(MYSQL_THD thd,const char * query)227 void sm_query_finished(MYSQL_THD thd,
228 const char* query __attribute__((unused))) {
229
230 sm_thd_data_t *thd_data= sm_thd_data_get(thd);
231 ulonglong end, save_busystart;
232
233 if (thd_data->start != 0)
234 {
235 save_busystart= busystart;
236 if (__sync_sub_and_fetch(&concurrency, 1) == 0)
237 {
238 end= sm_clock_time_get();
239 (void) __sync_add_and_fetch(&busytime,
240 sm_clock_time_duration(save_busystart, end));
241 }
242 else
243 {
244 end= sm_clock_time_get();
245 }
246
247 thd_data->duration+= sm_clock_time_duration(thd_data->start, end);
248 thd_data->queries++;
249 }
250 }
251
252 static
sm_query_failed(MYSQL_THD thd,const char * query,int err)253 void sm_query_failed(MYSQL_THD thd,
254 const char* query,
255 int err __attribute__((unused))) {
256
257 /* currently there is no difference between success and failure */
258
259 sm_query_finished(thd, query);
260
261 }
262
263
264 static
sm_elapsedtime(MYSQL_THD thd,struct st_mysql_show_var * var,char * buff)265 int sm_elapsedtime(MYSQL_THD thd __attribute__((unused)),
266 struct st_mysql_show_var* var,
267 char *buff)
268 {
269 *((ulonglong*)buff)= (sm_ctl == CTL_ON) ?
270 sm_clock_time_duration(starttime, sm_clock_time_get()) :
271 0;
272 var->type= SHOW_LONGLONG;
273 var->value= buff;
274 return(0);
275 }
276
277
278 static
sm_sum_queries(void * data,void * argument)279 int sm_sum_queries(void *data, void *argument)
280 {
281 sm_thd_data_t *thd_data= (sm_thd_data_t *) data;
282 *((ulonglong *) argument)+= thd_data->queries;
283 return(0);
284 }
285
286
287 static
sm_queries(MYSQL_THD thd,struct st_mysql_show_var * var,char * buff)288 int sm_queries(MYSQL_THD thd __attribute__((unused)),
289 struct st_mysql_show_var* var,
290 char *buff)
291 {
292 ulonglong sum_queries= 0;
293
294 if (sm_ctl == CTL_ON)
295 {
296 mysql_mutex_lock(&thd_list_mutex);
297 list_walk(thd_list_root, sm_sum_queries, (unsigned char *) &sum_queries);
298 mysql_mutex_unlock(&thd_list_mutex);
299 }
300 *((ulonglong *) buff)= queries + sum_queries;
301 var->type= SHOW_LONGLONG;
302 var->value= buff;
303 return(0);
304 }
305
306
307 static
sm_sum_totaltime(void * data,void * argument)308 int sm_sum_totaltime(void *data, void *argument)
309 {
310 sm_thd_data_t *thd_data= (sm_thd_data_t *) data;
311 *((ulonglong *) argument)+= thd_data->duration;
312 return(0);
313 }
314
315
316 static
sm_totaltime(MYSQL_THD thd,struct st_mysql_show_var * var,char * buff)317 int sm_totaltime(MYSQL_THD thd __attribute__((unused)),
318 struct st_mysql_show_var* var,
319 char *buff)
320 {
321 ulonglong sum_totaltime= 0;
322
323 if (sm_ctl == CTL_ON)
324 {
325 mysql_mutex_lock(&thd_list_mutex);
326 list_walk(thd_list_root, sm_sum_totaltime,
327 (unsigned char *) &sum_totaltime);
328 mysql_mutex_unlock(&thd_list_mutex);
329 }
330 *((ulonglong *) buff)= totaltime + sum_totaltime;
331 var->type= SHOW_LONGLONG;
332 var->value= buff;
333 return(0);
334 }
335
336
sm_notify(MYSQL_THD thd,unsigned int event_class,const void * event)337 static void sm_notify(MYSQL_THD thd, unsigned int event_class,
338 const void *event)
339 {
340
341 if (event_class == MYSQL_AUDIT_GENERAL_CLASS)
342 {
343 const struct mysql_event_general *event_general=
344 (const struct mysql_event_general *) event;
345
346 if (sm_ctl != CTL_ON)
347 {
348 return;
349 }
350
351 if (event_general->general_command &&
352 event_general->event_subclass == MYSQL_AUDIT_GENERAL_LOG &&
353 strcmp(event_general->general_command, "Query") == 0)
354 {
355 sm_query_started(thd, event_general->general_query);
356 }
357 else if (event_general->general_command &&
358 event_general->event_subclass == MYSQL_AUDIT_GENERAL_LOG &&
359 strcmp(event_general->general_command, "Execute") == 0)
360 {
361 sm_query_started(thd, event_general->general_query);
362 }
363 else if (event_general->general_query &&
364 event_general->event_subclass == MYSQL_AUDIT_GENERAL_RESULT)
365 {
366 sm_query_finished(thd, event_general->general_query);
367 }
368 else if (event_general->general_query &&
369 event_general->event_subclass == MYSQL_AUDIT_GENERAL_ERROR)
370 {
371 sm_query_failed(thd, event_general->general_query,
372 event_general->general_error_code);
373 }
374
375 }
376 else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
377 {
378 const struct mysql_event_connection *event_connection=
379 (const struct mysql_event_connection *) event;
380 switch (event_connection->event_subclass)
381 {
382 case MYSQL_AUDIT_CONNECTION_CONNECT:
383 sm_thd_data_get(thd);
384 break;
385 case MYSQL_AUDIT_CONNECTION_DISCONNECT:
386 sm_thd_data_release(thd);
387 break;
388 default:
389 break;
390 }
391 }
392 }
393
394 /*
395 * Plugin system vars
396 */
397 static struct st_mysql_sys_var* scalability_metrics_system_variables[] =
398 {
399 MYSQL_SYSVAR(thd_data),
400 MYSQL_SYSVAR(control),
401 NULL
402 };
403
404 /*
405 Plugin type-specific descriptor
406 */
407 static struct st_mysql_audit scalability_metrics_descriptor=
408 {
409 MYSQL_AUDIT_INTERFACE_VERSION, /* interface version */
410 NULL, /* release_thd function */
411 sm_notify, /* notify function */
412 { MYSQL_AUDIT_GENERAL_CLASSMASK |
413 MYSQL_AUDIT_CONNECTION_CLASSMASK } /* class mask */
414 };
415
416 /*
417 Plugin status variables for SHOW STATUS
418 */
419
420 static struct st_mysql_show_var simple_status[]=
421 {
422 { "scalability_metrics_elapsedtime", (char *) &sm_elapsedtime, SHOW_FUNC },
423 { "scalability_metrics_queries", (char *) &sm_queries, SHOW_FUNC },
424 { "scalability_metrics_concurrency", (char *) &concurrency, SHOW_LONGLONG },
425 { "scalability_metrics_totaltime", (char *) &sm_totaltime, SHOW_FUNC },
426 { "scalability_metrics_busytime", (char *) &busytime, SHOW_LONGLONG },
427 { 0, 0, 0}
428 };
429
430
431 /*
432 Plugin library descriptor
433 */
434
mysql_declare_plugin(scalability_metrics)435 mysql_declare_plugin(scalability_metrics)
436 {
437 MYSQL_AUDIT_PLUGIN, /* type */
438 &scalability_metrics_descriptor, /* descriptor */
439 "scalability_metrics", /* name */
440 "Percona LLC and/or its affiliates", /* author */
441 "Scalability metrics", /* description */
442 PLUGIN_LICENSE_GPL,
443 sm_plugin_init, /* init function (when loaded) */
444 sm_plugin_deinit, /* deinit function (when unloaded) */
445 0x0001, /* version */
446 simple_status, /* status variables */
447 scalability_metrics_system_variables, /* system variables */
448 NULL,
449 0,
450 }
451 mysql_declare_plugin_end;
452
453