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_server.cc
25   Private interface for the server (implementation).
26 */
27 
28 #include "my_global.h"
29 #include "my_sys.h"
30 #include "mysys_err.h"
31 #include "pfs_server.h"
32 #include "pfs.h"
33 #include "pfs_global.h"
34 #include "pfs_instr_class.h"
35 #include "pfs_builtin_memory.h"
36 #include "pfs_instr.h"
37 #include "pfs_events_waits.h"
38 #include "pfs_events_stages.h"
39 #include "pfs_events_statements.h"
40 #include "pfs_events_transactions.h"
41 #include "pfs_timer.h"
42 #include "pfs_setup_actor.h"
43 #include "pfs_setup_object.h"
44 #include "pfs_host.h"
45 #include "pfs_user.h"
46 #include "pfs_account.h"
47 #include "pfs_defaults.h"
48 #include "pfs_digest.h"
49 #include "pfs_program.h"
50 #include "template_utils.h"
51 #include "pfs_prepared_stmt.h"
52 
53 PFS_global_param pfs_param;
54 
55 PFS_table_stat PFS_table_stat::g_reset_template;
56 
57 C_MODE_START
58 static void destroy_pfs_thread(void *key);
59 C_MODE_END
60 
61 static void cleanup_performance_schema(void);
62 void cleanup_instrument_config(void);
63 
pre_initialize_performance_schema()64 void pre_initialize_performance_schema()
65 {
66   pfs_initialized= false;
67 
68   init_all_builtin_memory_class();
69 
70   PFS_table_stat::g_reset_template.reset();
71   global_idle_stat.reset();
72   global_table_io_stat.reset();
73   global_table_lock_stat.reset();
74 
75   if (my_create_thread_local_key(&THR_PFS, destroy_pfs_thread))
76     return;
77   if (my_create_thread_local_key(&THR_PFS_VG, NULL))  // global_variables
78     return;
79   if (my_create_thread_local_key(&THR_PFS_SV, NULL))  // session_variables
80     return;
81   if (my_create_thread_local_key(&THR_PFS_VBT, NULL)) // variables_by_thread
82     return;
83   if (my_create_thread_local_key(&THR_PFS_SG, NULL))  // global_status
84     return;
85   if (my_create_thread_local_key(&THR_PFS_SS, NULL))  // session_status
86     return;
87   if (my_create_thread_local_key(&THR_PFS_SBT, NULL)) // status_by_thread
88     return;
89   if (my_create_thread_local_key(&THR_PFS_SBU, NULL)) // status_by_user
90     return;
91   if (my_create_thread_local_key(&THR_PFS_SBH, NULL)) // status_by_host
92     return;
93   if (my_create_thread_local_key(&THR_PFS_SBA, NULL)) // status_by_account
94     return;
95 
96   THR_PFS_initialized= true;
97 }
98 
99 struct PSI_bootstrap*
initialize_performance_schema(PFS_global_param * param)100 initialize_performance_schema(PFS_global_param *param)
101 {
102   if (!THR_PFS_initialized)
103   {
104     /* Pre-initialization failed. */
105     return NULL;
106   }
107 
108   pfs_enabled= param->m_enabled;
109 
110   pfs_automated_sizing(param);
111   init_timers();
112   init_event_name_sizing(param);
113   register_global_classes();
114 
115   if (init_sync_class(param->m_mutex_class_sizing,
116                       param->m_rwlock_class_sizing,
117                       param->m_cond_class_sizing) ||
118       init_thread_class(param->m_thread_class_sizing) ||
119       init_table_share(param->m_table_share_sizing) ||
120       init_table_share_lock_stat(param->m_table_lock_stat_sizing) ||
121       init_table_share_index_stat(param->m_index_stat_sizing) ||
122       init_file_class(param->m_file_class_sizing) ||
123       init_stage_class(param->m_stage_class_sizing) ||
124       init_statement_class(param->m_statement_class_sizing) ||
125       init_socket_class(param->m_socket_class_sizing) ||
126       init_memory_class(param->m_memory_class_sizing) ||
127       init_instruments(param) ||
128       init_events_waits_history_long(
129         param->m_events_waits_history_long_sizing) ||
130       init_events_stages_history_long(
131         param->m_events_stages_history_long_sizing) ||
132       init_events_statements_history_long(
133         param->m_events_statements_history_long_sizing) ||
134       init_events_transactions_history_long(
135         param->m_events_transactions_history_long_sizing) ||
136       init_file_hash(param) ||
137       init_table_share_hash(param) ||
138       init_setup_actor(param) ||
139       init_setup_actor_hash(param) ||
140       init_setup_object(param) ||
141       init_setup_object_hash(param) ||
142       init_host(param) ||
143       init_host_hash(param) ||
144       init_user(param) ||
145       init_user_hash(param) ||
146       init_account(param) ||
147       init_account_hash(param) ||
148       init_digest(param) ||
149       init_digest_hash(param) ||
150       init_program(param) ||
151       init_program_hash(param) ||
152       init_prepared_stmt(param))
153   {
154     /*
155       The performance schema initialization failed.
156       Free the memory used, and disable the instrumentation.
157     */
158     cleanup_performance_schema();
159     return NULL;
160   }
161 
162   if (param->m_enabled)
163   {
164     /** Default values for SETUP_CONSUMERS */
165     flag_events_stages_current=            param->m_consumer_events_stages_current_enabled;
166     flag_events_stages_history=            param->m_consumer_events_stages_history_enabled;
167     flag_events_stages_history_long=       param->m_consumer_events_stages_history_long_enabled;
168     flag_events_statements_current=        param->m_consumer_events_statements_current_enabled;
169     flag_events_statements_history=        param->m_consumer_events_statements_history_enabled;
170     flag_events_statements_history_long=   param->m_consumer_events_statements_history_long_enabled;
171     flag_events_transactions_current=      param->m_consumer_events_transactions_current_enabled;
172     flag_events_transactions_history=      param->m_consumer_events_transactions_history_enabled;
173     flag_events_transactions_history_long= param->m_consumer_events_transactions_history_long_enabled;
174     flag_events_waits_current=             param->m_consumer_events_waits_current_enabled;
175     flag_events_waits_history=             param->m_consumer_events_waits_history_enabled;
176     flag_events_waits_history_long=        param->m_consumer_events_waits_history_long_enabled;
177     flag_global_instrumentation=           param->m_consumer_global_instrumentation_enabled;
178     flag_thread_instrumentation=           param->m_consumer_thread_instrumentation_enabled;
179     flag_statements_digest=                param->m_consumer_statement_digest_enabled;
180   }
181   else
182   {
183     flag_events_stages_current= false;
184     flag_events_stages_history= false;
185     flag_events_stages_history_long= false;
186     flag_events_statements_current= false;
187     flag_events_statements_history= false;
188     flag_events_statements_history_long= false;
189     flag_events_transactions_current= false;
190     flag_events_transactions_history= false;
191     flag_events_transactions_history_long= false;
192     flag_events_waits_current= false;
193     flag_events_waits_history= false;
194     flag_events_waits_history_long= false;
195     flag_global_instrumentation= false;
196     flag_thread_instrumentation= false;
197     flag_statements_digest= false;
198   }
199 
200   pfs_initialized= true;
201 
202   if (param->m_enabled)
203   {
204     install_default_setup(&PFS_bootstrap);
205     return &PFS_bootstrap;
206   }
207 
208   return NULL;
209 }
210 
destroy_pfs_thread(void * key)211 static void destroy_pfs_thread(void *key)
212 {
213   PFS_thread* pfs= reinterpret_cast<PFS_thread*> (key);
214   assert(pfs);
215   /*
216     This automatic cleanup is a last resort and best effort to avoid leaks,
217     and may not work on windows due to the implementation of pthread_key_create().
218     Please either use:
219     - my_thread_end()
220     - or PSI_server->delete_current_thread()
221     in the instrumented code, to explicitly cleanup the instrumentation.
222 
223     Avoid invalid writes when the main() thread completes after shutdown:
224     the memory pointed by pfs is already released.
225   */
226   if (pfs_initialized)
227     destroy_thread(pfs);
228 }
229 
cleanup_performance_schema(void)230 static void cleanup_performance_schema(void)
231 {
232   /*
233     my.cnf options
234   */
235 
236   cleanup_instrument_config();
237 
238   /*
239     All the LF_HASH
240   */
241 
242   cleanup_setup_actor_hash();
243   cleanup_setup_object_hash();
244   cleanup_account_hash();
245   cleanup_host_hash();
246   cleanup_user_hash();
247   cleanup_program_hash();
248   cleanup_table_share_hash();
249   cleanup_file_hash();
250   cleanup_digest_hash();
251 
252   /*
253     Then the lookup tables
254   */
255 
256   cleanup_setup_actor();
257   cleanup_setup_object();
258 
259   /*
260     Then the history tables
261   */
262 
263   cleanup_events_waits_history_long();
264   cleanup_events_stages_history_long();
265   cleanup_events_statements_history_long();
266   cleanup_events_transactions_history_long();
267 
268   /*
269     Then the various aggregations
270   */
271 
272   cleanup_digest();
273   cleanup_account();
274   cleanup_host();
275   cleanup_user();
276 
277   /*
278     Then the instrument classes.
279     Once a class is cleaned up,
280     find_XXX_class(key)
281     will return PSI_NOT_INSTRUMENTED
282   */
283   cleanup_program();
284   cleanup_prepared_stmt();
285   cleanup_sync_class();
286   cleanup_thread_class();
287   cleanup_table_share();
288   cleanup_table_share_lock_stat();
289   cleanup_table_share_index_stat();
290   cleanup_file_class();
291   cleanup_stage_class();
292   cleanup_statement_class();
293   cleanup_socket_class();
294   cleanup_memory_class();
295 
296   cleanup_instruments();
297 }
298 
shutdown_performance_schema(void)299 void shutdown_performance_schema(void)
300 {
301   pfs_initialized= false;
302 
303   /* disable everything, especially for this thread. */
304   flag_events_stages_current= false;
305   flag_events_stages_history= false;
306   flag_events_stages_history_long= false;
307   flag_events_statements_current= false;
308   flag_events_statements_history= false;
309   flag_events_statements_history_long= false;
310   flag_events_transactions_current= false;
311   flag_events_transactions_history= false;
312   flag_events_transactions_history_long= false;
313   flag_events_waits_current= false;
314   flag_events_waits_history= false;
315   flag_events_waits_history_long= false;
316   flag_global_instrumentation= false;
317   flag_thread_instrumentation= false;
318   flag_statements_digest= false;
319 
320   global_table_io_class.m_enabled= false;
321   global_table_lock_class.m_enabled= false;
322   global_idle_class.m_enabled= false;
323   global_metadata_class.m_enabled= false;
324   global_transaction_class.m_enabled= false;
325 
326   cleanup_performance_schema();
327   /*
328     Be careful to not delete un-initialized keys,
329     this would affect key 0, which is THR_KEY_mysys,
330   */
331   if (THR_PFS_initialized)
332   {
333     my_set_thread_local(THR_PFS, NULL);
334     my_set_thread_local(THR_PFS_VG, NULL);  // global_variables
335     my_set_thread_local(THR_PFS_SV, NULL);  // session_variables
336     my_set_thread_local(THR_PFS_VBT, NULL); // variables_by_thread
337     my_set_thread_local(THR_PFS_SG, NULL);  // global_status
338     my_set_thread_local(THR_PFS_SS, NULL);  // session_status
339     my_set_thread_local(THR_PFS_SBT, NULL); // status_by_thread
340     my_set_thread_local(THR_PFS_SBU, NULL); // status_by_user
341     my_set_thread_local(THR_PFS_SBH, NULL); // status_by_host
342     my_set_thread_local(THR_PFS_SBA, NULL); // status_by_account
343 
344     my_delete_thread_local_key(THR_PFS);
345     my_delete_thread_local_key(THR_PFS_VG);
346     my_delete_thread_local_key(THR_PFS_SV);
347     my_delete_thread_local_key(THR_PFS_VBT);
348     my_delete_thread_local_key(THR_PFS_SG);
349     my_delete_thread_local_key(THR_PFS_SS);
350     my_delete_thread_local_key(THR_PFS_SBT);
351     my_delete_thread_local_key(THR_PFS_SBU);
352     my_delete_thread_local_key(THR_PFS_SBH);
353     my_delete_thread_local_key(THR_PFS_SBA);
354 
355     THR_PFS_initialized= false;
356   }
357 }
358 
359 /**
360   Initialize the dynamic array used to hold PFS_INSTRUMENT configuration
361   options.
362 */
init_pfs_instrument_array()363 void init_pfs_instrument_array()
364 {
365   pfs_instr_config_array= new Pfs_instr_config_array(PSI_NOT_INSTRUMENTED);
366 }
367 
368 /**
369   Deallocate the PFS_INSTRUMENT array.
370 */
cleanup_instrument_config()371 void cleanup_instrument_config()
372 {
373   if (pfs_instr_config_array != NULL)
374     my_free_container_pointers(*pfs_instr_config_array);
375   delete pfs_instr_config_array;
376   pfs_instr_config_array= NULL;
377 }
378 
379 /**
380   Process one performance_schema_instrument configuration string. Isolate the
381   instrument name, evaluate the option value, and store them in a dynamic array.
382   Return 'false' for success, 'true' for error.
383 
384   @param name    Instrument name
385   @param value   Configuration option: 'on', 'off', etc.
386   @return 0 for success, non zero for errors
387 */
388 
add_pfs_instr_to_array(const char * name,const char * value)389 int add_pfs_instr_to_array(const char* name, const char* value)
390 {
391   size_t name_length= strlen(name);
392   size_t value_length= strlen(value);
393 
394   /* Allocate structure plus string buffers plus null terminators */
395   PFS_instr_config* e = (PFS_instr_config*)my_malloc(PSI_NOT_INSTRUMENTED,
396                                                      sizeof(PFS_instr_config)
397                        + name_length + 1 + value_length + 1, MYF(MY_WME));
398   if (!e) return 1;
399 
400   /* Copy the instrument name */
401   e->m_name= (char*)e + sizeof(PFS_instr_config);
402   memcpy(e->m_name, name, name_length);
403   e->m_name_length= (uint)name_length;
404   e->m_name[name_length]= '\0';
405 
406   /* Set flags accordingly */
407   if (!my_strcasecmp(&my_charset_latin1, value, "counted"))
408   {
409     e->m_enabled= true;
410     e->m_timed= false;
411   }
412   else
413   if (!my_strcasecmp(&my_charset_latin1, value, "true") ||
414       !my_strcasecmp(&my_charset_latin1, value, "on") ||
415       !my_strcasecmp(&my_charset_latin1, value, "1") ||
416       !my_strcasecmp(&my_charset_latin1, value, "yes"))
417   {
418     e->m_enabled= true;
419     e->m_timed= true;
420   }
421   else
422   if (!my_strcasecmp(&my_charset_latin1, value, "false") ||
423       !my_strcasecmp(&my_charset_latin1, value, "off") ||
424       !my_strcasecmp(&my_charset_latin1, value, "0") ||
425       !my_strcasecmp(&my_charset_latin1, value, "no"))
426   {
427     e->m_enabled= false;
428     e->m_timed= false;
429   }
430   else
431   {
432     my_free(e);
433     return 1;
434   }
435 
436   /* Add to the array of default startup options */
437   if (pfs_instr_config_array->push_back(e))
438   {
439     my_free(e);
440     return 1;
441   }
442 
443   return 0;
444 }
445