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