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