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