1 /* Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
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_instr.h"
36 #include "pfs_events_waits.h"
37 #include "pfs_events_stages.h"
38 #include "pfs_events_statements.h"
39 #include "pfs_timer.h"
40 #include "pfs_setup_actor.h"
41 #include "pfs_setup_object.h"
42 #include "pfs_host.h"
43 #include "pfs_user.h"
44 #include "pfs_account.h"
45 #include "pfs_defaults.h"
46 #include "pfs_digest.h"
47 
48 PFS_global_param pfs_param;
49 
50 PFS_table_stat PFS_table_stat::g_reset_template;
51 
52 C_MODE_START
53 static void destroy_pfs_thread(void *key);
54 C_MODE_END
55 
56 static void cleanup_performance_schema(void);
57 void cleanup_instrument_config(void);
58 
59 struct PSI_bootstrap*
initialize_performance_schema(PFS_global_param * param)60 initialize_performance_schema(PFS_global_param *param)
61 {
62   pfs_initialized= false;
63 
64   PFS_table_stat::g_reset_template.reset();
65   global_idle_stat.reset();
66   global_table_io_stat.reset();
67   global_table_lock_stat.reset();
68 
69   pfs_automated_sizing(param);
70 
71   if (! param->m_enabled)
72   {
73     /*
74       The performance schema is disabled in the startup command line.
75       All the instrumentation is turned off.
76     */
77     return NULL;
78   }
79 
80   init_timers();
81   PFS_atomic::init();
82 
83   init_event_name_sizing(param);
84   register_global_classes();
85 
86   if (pthread_key_create(&THR_PFS, destroy_pfs_thread))
87     return NULL;
88 
89   THR_PFS_initialized= true;
90 
91   if (init_sync_class(param->m_mutex_class_sizing,
92                       param->m_rwlock_class_sizing,
93                       param->m_cond_class_sizing) ||
94       init_thread_class(param->m_thread_class_sizing) ||
95       init_table_share(param->m_table_share_sizing) ||
96       init_file_class(param->m_file_class_sizing) ||
97       init_stage_class(param->m_stage_class_sizing) ||
98       init_statement_class(param->m_statement_class_sizing) ||
99       init_socket_class(param->m_socket_class_sizing) ||
100       init_instruments(param) ||
101       init_events_waits_history_long(
102         param->m_events_waits_history_long_sizing) ||
103       init_events_stages_history_long(
104         param->m_events_stages_history_long_sizing) ||
105       init_events_statements_history_long(
106         param->m_events_statements_history_long_sizing) ||
107       init_file_hash() ||
108       init_table_share_hash() ||
109       init_setup_actor(param) ||
110       init_setup_actor_hash() ||
111       init_setup_object(param) ||
112       init_setup_object_hash() ||
113       init_host(param) ||
114       init_host_hash() ||
115       init_user(param) ||
116       init_user_hash() ||
117       init_account(param) ||
118       init_account_hash() ||
119       init_digest(param) ||
120       init_digest_hash())
121   {
122     /*
123       The performance schema initialization failed.
124       Free the memory used, and disable the instrumentation.
125     */
126     cleanup_performance_schema();
127     return NULL;
128   }
129 
130   pfs_initialized= true;
131 
132   /** Default values for SETUP_CONSUMERS */
133   flag_events_stages_current=          param->m_consumer_events_stages_current_enabled;
134   flag_events_stages_history=          param->m_consumer_events_stages_history_enabled;
135   flag_events_stages_history_long=     param->m_consumer_events_stages_history_long_enabled;
136   flag_events_statements_current=      param->m_consumer_events_statements_current_enabled;
137   flag_events_statements_history=      param->m_consumer_events_statements_history_enabled;
138   flag_events_statements_history_long= param->m_consumer_events_statements_history_long_enabled;
139   flag_events_waits_current=           param->m_consumer_events_waits_current_enabled;
140   flag_events_waits_history=           param->m_consumer_events_waits_history_enabled;
141   flag_events_waits_history_long=      param->m_consumer_events_waits_history_long_enabled;
142   flag_global_instrumentation=         param->m_consumer_global_instrumentation_enabled;
143   flag_thread_instrumentation=         param->m_consumer_thread_instrumentation_enabled;
144   flag_statements_digest=              param->m_consumer_statement_digest_enabled;
145 
146   install_default_setup(&PFS_bootstrap);
147   return &PFS_bootstrap;
148 }
149 
destroy_pfs_thread(void * key)150 static void destroy_pfs_thread(void *key)
151 {
152   PFS_thread* pfs= reinterpret_cast<PFS_thread*> (key);
153   DBUG_ASSERT(pfs);
154   /*
155     This automatic cleanup is a last resort and best effort to avoid leaks,
156     and may not work on windows due to the implementation of pthread_key_create().
157     Please either use:
158     - my_thread_end()
159     - or PSI_server->delete_current_thread()
160     in the instrumented code, to explicitly cleanup the instrumentation.
161 
162     Avoid invalid writes when the main() thread completes after shutdown:
163     the memory pointed by pfs is already released.
164   */
165   if (pfs_initialized)
166     destroy_thread(pfs);
167 }
168 
cleanup_performance_schema(void)169 static void cleanup_performance_schema(void)
170 {
171   cleanup_instrument_config();
172 /*  Disabled: Bug#5666
173   cleanup_instruments();
174   cleanup_sync_class();
175   cleanup_thread_class();
176   cleanup_table_share();
177   cleanup_file_class();
178   cleanup_stage_class();
179   cleanup_statement_class();
180   cleanup_socket_class();
181   cleanup_events_waits_history_long();
182   cleanup_events_stages_history_long();
183   cleanup_events_statements_history_long();
184   cleanup_table_share_hash();
185   cleanup_file_hash();
186   cleanup_setup_actor();
187   cleanup_setup_actor_hash();
188   cleanup_setup_object();
189   cleanup_setup_object_hash();
190   cleanup_host();
191   cleanup_host_hash();
192   cleanup_user();
193   cleanup_user_hash();
194   cleanup_account();
195   cleanup_account_hash();
196   cleanup_digest();
197   PFS_atomic::cleanup();
198 */
199 }
200 
shutdown_performance_schema(void)201 void shutdown_performance_schema(void)
202 {
203   pfs_initialized= false;
204   cleanup_performance_schema();
205 #if 0
206   /*
207     Be careful to not delete un-initialized keys,
208     this would affect key 0, which is THR_KEY_mysys,
209   */
210   if (THR_PFS_initialized)
211   {
212     my_pthread_setspecific_ptr(THR_PFS, NULL);
213     pthread_key_delete(THR_PFS);
214     THR_PFS_initialized= false;
215   }
216 #endif
217 }
218 
219 /**
220   Initialize the dynamic array used to hold PFS_INSTRUMENT configuration
221   options.
222 */
init_pfs_instrument_array()223 void init_pfs_instrument_array()
224 {
225   my_init_dynamic_array(&pfs_instr_config_array, sizeof(PFS_instr_config*), 10, 10);
226   pfs_instr_config_state=  PFS_INSTR_CONFIG_ALLOCATED;
227 }
228 
229 /**
230   Deallocate the PFS_INSTRUMENT array. Use an atomic compare-and-swap to ensure
231   that it is deallocated only once in the chaotic environment of server shutdown.
232 */
cleanup_instrument_config()233 void cleanup_instrument_config()
234 {
235   int desired_state= PFS_INSTR_CONFIG_ALLOCATED;
236 
237   /* Ignore if another thread has already deallocated the array */
238   if (my_atomic_cas32(&pfs_instr_config_state, &desired_state, PFS_INSTR_CONFIG_DEALLOCATED))
239     delete_dynamic(&pfs_instr_config_array);
240 }
241 
242 /**
243   Process one performance_schema_instrument configuration string. Isolate the
244   instrument name, evaluate the option value, and store them in a dynamic array.
245   Return 'false' for success, 'true' for error.
246 
247   @param name    Instrument name
248   @param value   Configuration option: 'on', 'off', etc.
249   @return 0 for success, non zero for errors
250 */
251 
add_pfs_instr_to_array(const char * name,const char * value)252 int add_pfs_instr_to_array(const char* name, const char* value)
253 {
254   int name_length= strlen(name);
255   int value_length= strlen(value);
256 
257   /* Allocate structure plus string buffers plus null terminators */
258   PFS_instr_config* e = (PFS_instr_config*)my_malloc(sizeof(PFS_instr_config)
259                        + name_length + 1 + value_length + 1, MYF(MY_WME));
260   if (!e) return 1;
261 
262   /* Copy the instrument name */
263   e->m_name= (char*)e + sizeof(PFS_instr_config);
264   memcpy(e->m_name, name, name_length);
265   e->m_name_length= name_length;
266   e->m_name[name_length]= '\0';
267 
268   /* Set flags accordingly */
269   if (!my_strcasecmp(&my_charset_latin1, value, "counted"))
270   {
271     e->m_enabled= true;
272     e->m_timed= false;
273   }
274   else
275   if (!my_strcasecmp(&my_charset_latin1, value, "true") ||
276       !my_strcasecmp(&my_charset_latin1, value, "on") ||
277       !my_strcasecmp(&my_charset_latin1, value, "1") ||
278       !my_strcasecmp(&my_charset_latin1, value, "yes"))
279   {
280     e->m_enabled= true;
281     e->m_timed= true;
282   }
283   else
284   if (!my_strcasecmp(&my_charset_latin1, value, "false") ||
285       !my_strcasecmp(&my_charset_latin1, value, "off") ||
286       !my_strcasecmp(&my_charset_latin1, value, "0") ||
287       !my_strcasecmp(&my_charset_latin1, value, "no"))
288   {
289     e->m_enabled= false;
290     e->m_timed= false;
291   }
292   else
293   {
294     my_free(e);
295     return 1;
296   }
297 
298   /* Add to the array of default startup options */
299   if (insert_dynamic(&pfs_instr_config_array, &e))
300   {
301     my_free(e);
302     return 1;
303   }
304 
305   return 0;
306 }
307