1 /* Copyright (c) 2010, 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
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
22
23 /**
24 @file storage/perfschema/pfs_user.cc
25 Performance schema user (implementation).
26 */
27
28 #include "my_global.h"
29 #include "my_sys.h"
30 #include "pfs.h"
31 #include "pfs_stat.h"
32 #include "pfs_instr.h"
33 #include "pfs_setup_actor.h"
34 #include "pfs_user.h"
35 #include "pfs_global.h"
36 #include "pfs_instr_class.h"
37
38 /**
39 @addtogroup Performance_schema_buffers
40 @{
41 */
42
43 ulong user_max;
44 ulong user_lost;
45
46 PFS_user *user_array= NULL;
47
48 static PFS_single_stat *user_instr_class_waits_array= NULL;
49 static PFS_stage_stat *user_instr_class_stages_array= NULL;
50 static PFS_statement_stat *user_instr_class_statements_array= NULL;
51
52 LF_HASH user_hash;
53 static bool user_hash_inited= false;
54
55 /**
56 Initialize the user buffers.
57 @param param sizing parameters
58 @return 0 on success
59 */
init_user(const PFS_global_param * param)60 int init_user(const PFS_global_param *param)
61 {
62 uint index;
63
64 user_max= param->m_user_sizing;
65
66 user_array= NULL;
67 user_instr_class_waits_array= NULL;
68 user_instr_class_stages_array= NULL;
69 user_instr_class_statements_array= NULL;
70 uint waits_sizing= user_max * wait_class_max;
71 uint stages_sizing= user_max * stage_class_max;
72 uint statements_sizing= user_max * statement_class_max;
73
74 if (user_max > 0)
75 {
76 user_array= PFS_MALLOC_ARRAY(user_max, sizeof(PFS_user), PFS_user,
77 MYF(MY_ZEROFILL));
78 if (unlikely(user_array == NULL))
79 return 1;
80 }
81
82 if (waits_sizing > 0)
83 {
84 user_instr_class_waits_array=
85 PFS_connection_slice::alloc_waits_slice(waits_sizing);
86 if (unlikely(user_instr_class_waits_array == NULL))
87 return 1;
88 }
89
90 if (stages_sizing > 0)
91 {
92 user_instr_class_stages_array=
93 PFS_connection_slice::alloc_stages_slice(stages_sizing);
94 if (unlikely(user_instr_class_stages_array == NULL))
95 return 1;
96 }
97
98 if (statements_sizing > 0)
99 {
100 user_instr_class_statements_array=
101 PFS_connection_slice::alloc_statements_slice(statements_sizing);
102 if (unlikely(user_instr_class_statements_array == NULL))
103 return 1;
104 }
105
106 for (index= 0; index < user_max; index++)
107 {
108 user_array[index].m_instr_class_waits_stats=
109 &user_instr_class_waits_array[index * wait_class_max];
110 user_array[index].m_instr_class_stages_stats=
111 &user_instr_class_stages_array[index * stage_class_max];
112 user_array[index].m_instr_class_statements_stats=
113 &user_instr_class_statements_array[index * statement_class_max];
114 }
115
116 return 0;
117 }
118
119 /** Cleanup all the user buffers. */
cleanup_user(void)120 void cleanup_user(void)
121 {
122 pfs_free(user_array);
123 user_array= NULL;
124 pfs_free(user_instr_class_waits_array);
125 user_instr_class_waits_array= NULL;
126 pfs_free(user_instr_class_stages_array);
127 user_instr_class_stages_array= NULL;
128 pfs_free(user_instr_class_statements_array);
129 user_instr_class_statements_array= NULL;
130 user_max= 0;
131 }
132
133 C_MODE_START
user_hash_get_key(const uchar * entry,size_t * length,my_bool)134 static uchar *user_hash_get_key(const uchar *entry, size_t *length,
135 my_bool)
136 {
137 const PFS_user * const *typed_entry;
138 const PFS_user *user;
139 const void *result;
140 typed_entry= reinterpret_cast<const PFS_user* const *> (entry);
141 DBUG_ASSERT(typed_entry != NULL);
142 user= *typed_entry;
143 DBUG_ASSERT(user != NULL);
144 *length= user->m_key.m_key_length;
145 result= user->m_key.m_hash_key;
146 return const_cast<uchar*> (reinterpret_cast<const uchar*> (result));
147 }
148 C_MODE_END
149
150 /**
151 Initialize the user hash.
152 @return 0 on success
153 */
init_user_hash(void)154 int init_user_hash(void)
155 {
156 if ((! user_hash_inited) && (user_max > 0))
157 {
158 lf_hash_init(&user_hash, sizeof(PFS_user*), LF_HASH_UNIQUE,
159 0, 0, user_hash_get_key, &my_charset_bin);
160 user_hash.size= user_max;
161 user_hash_inited= true;
162 }
163 return 0;
164 }
165
166 /** Cleanup the user hash. */
cleanup_user_hash(void)167 void cleanup_user_hash(void)
168 {
169 if (user_hash_inited)
170 {
171 lf_hash_destroy(&user_hash);
172 user_hash_inited= false;
173 }
174 }
175
get_user_hash_pins(PFS_thread * thread)176 static LF_PINS* get_user_hash_pins(PFS_thread *thread)
177 {
178 if (unlikely(thread->m_user_hash_pins == NULL))
179 {
180 if (! user_hash_inited)
181 return NULL;
182 thread->m_user_hash_pins= lf_hash_get_pins(&user_hash);
183 }
184 return thread->m_user_hash_pins;
185 }
186
set_user_key(PFS_user_key * key,const char * user,uint user_length)187 static void set_user_key(PFS_user_key *key,
188 const char *user, uint user_length)
189 {
190 DBUG_ASSERT(user_length <= USERNAME_LENGTH);
191
192 char *ptr= &key->m_hash_key[0];
193 if (user_length > 0)
194 {
195 memcpy(ptr, user, user_length);
196 ptr+= user_length;
197 }
198 ptr[0]= 0;
199 ptr++;
200 key->m_key_length= ptr - &key->m_hash_key[0];
201 }
202
203 PFS_user *
find_or_create_user(PFS_thread * thread,const char * username,uint username_length)204 find_or_create_user(PFS_thread *thread,
205 const char *username, uint username_length)
206 {
207 if (user_max == 0)
208 {
209 user_lost++;
210 return NULL;
211 }
212
213 LF_PINS *pins= get_user_hash_pins(thread);
214 if (unlikely(pins == NULL))
215 {
216 user_lost++;
217 return NULL;
218 }
219
220 PFS_user_key key;
221 set_user_key(&key, username, username_length);
222
223 PFS_user **entry;
224 uint retry_count= 0;
225 const uint retry_max= 3;
226
227 search:
228 entry= reinterpret_cast<PFS_user**>
229 (lf_hash_search(&user_hash, pins,
230 key.m_hash_key, key.m_key_length));
231 if (entry && (entry != MY_ERRPTR))
232 {
233 PFS_user *pfs;
234 pfs= *entry;
235 pfs->inc_refcount();
236 lf_hash_search_unpin(pins);
237 return pfs;
238 }
239
240 lf_hash_search_unpin(pins);
241
242 PFS_scan scan;
243 uint random= randomized_index(username, user_max);
244
245 for (scan.init(random, user_max);
246 scan.has_pass();
247 scan.next_pass())
248 {
249 PFS_user *pfs= user_array + scan.first();
250 PFS_user *pfs_last= user_array + scan.last();
251 for ( ; pfs < pfs_last; pfs++)
252 {
253 if (pfs->m_lock.is_free())
254 {
255 if (pfs->m_lock.free_to_dirty())
256 {
257 pfs->m_key= key;
258 if (username_length > 0)
259 pfs->m_username= &pfs->m_key.m_hash_key[0];
260 else
261 pfs->m_username= NULL;
262 pfs->m_username_length= username_length;
263
264 pfs->init_refcount();
265 pfs->reset_stats();
266 pfs->m_disconnected_count= 0;
267
268 int res;
269 res= lf_hash_insert(&user_hash, pins, &pfs);
270 if (likely(res == 0))
271 {
272 pfs->m_lock.dirty_to_allocated();
273 return pfs;
274 }
275
276 pfs->m_lock.dirty_to_free();
277
278 if (res > 0)
279 {
280 if (++retry_count > retry_max)
281 {
282 user_lost++;
283 return NULL;
284 }
285 goto search;
286 }
287
288 user_lost++;
289 return NULL;
290 }
291 }
292 }
293 }
294
295 user_lost++;
296 return NULL;
297 }
298
aggregate()299 void PFS_user::aggregate()
300 {
301 aggregate_waits();
302 aggregate_stages();
303 aggregate_statements();
304 aggregate_stats();
305 }
306
aggregate_waits()307 void PFS_user::aggregate_waits()
308 {
309 /* No parent to aggregate to, clean the stats */
310 reset_waits_stats();
311 }
312
aggregate_stages()313 void PFS_user::aggregate_stages()
314 {
315 /* No parent to aggregate to, clean the stats */
316 reset_stages_stats();
317 }
318
aggregate_statements()319 void PFS_user::aggregate_statements()
320 {
321 /* No parent to aggregate to, clean the stats */
322 reset_statements_stats();
323 }
324
aggregate_stats()325 void PFS_user::aggregate_stats()
326 {
327 /* No parent to aggregate to, clean the stats */
328 m_disconnected_count= 0;
329 }
330
release()331 void PFS_user::release()
332 {
333 dec_refcount();
334 }
335
sanitize_user(PFS_user * unsafe)336 PFS_user *sanitize_user(PFS_user *unsafe)
337 {
338 if ((&user_array[0] <= unsafe) &&
339 (unsafe < &user_array[user_max]))
340 return unsafe;
341 return NULL;
342 }
343
purge_user(PFS_thread * thread,PFS_user * user)344 void purge_user(PFS_thread *thread, PFS_user *user)
345 {
346 LF_PINS *pins= get_user_hash_pins(thread);
347 if (unlikely(pins == NULL))
348 return;
349
350 PFS_user **entry;
351 entry= reinterpret_cast<PFS_user**>
352 (lf_hash_search(&user_hash, pins,
353 user->m_key.m_hash_key, user->m_key.m_key_length));
354 if (entry && (entry != MY_ERRPTR))
355 {
356 DBUG_ASSERT(*entry == user);
357 if (user->get_refcount() == 0)
358 {
359 lf_hash_delete(&user_hash, pins,
360 user->m_key.m_hash_key, user->m_key.m_key_length);
361 user->m_lock.allocated_to_free();
362 }
363 }
364
365 lf_hash_search_unpin(pins);
366 }
367
368 /** Purge non connected users, reset stats of connected users. */
purge_all_user(void)369 void purge_all_user(void)
370 {
371 PFS_thread *thread= PFS_thread::get_current_thread();
372 if (unlikely(thread == NULL))
373 return;
374
375 PFS_user *pfs= user_array;
376 PFS_user *pfs_last= user_array + user_max;
377
378 for ( ; pfs < pfs_last; pfs++)
379 {
380 if (pfs->m_lock.is_populated())
381 {
382 pfs->aggregate();
383 if (pfs->get_refcount() == 0)
384 purge_user(thread, pfs);
385 }
386 }
387 }
388
389 /** @} */
390