1/* Copyright (c) 2008, 2017, 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_digest.h
18 Statement Digest data structures (implementation).
19*/
20
21/*
22 This code needs extra visibility in the lexer structures
23*/
24
25#define MYSQL_LEX 1
26
27#include "my_global.h"
28#include "my_sys.h"
29#include "pfs_instr.h"
30#include "pfs_digest.h"
31#include "pfs_global.h"
32#include "table_helper.h"
33#include "sql_lex.h"
34#include "sql_get_diagnostics.h"
35#include "sql_string.h"
36#include <string.h>
37
38size_t digest_max= 0;
39ulong digest_lost= 0;
40
41/** EVENTS_STATEMENTS_HISTORY_LONG circular buffer. */
42PFS_statements_digest_stat *statements_digest_stat_array= NULL;
43static unsigned char *statements_digest_token_array= NULL;
44/** Consumer flag for table EVENTS_STATEMENTS_SUMMARY_BY_DIGEST. */
45bool flag_statements_digest= true;
46/**
47 Current index in Stat array where new record is to be inserted.
48 index 0 is reserved for "all else" case when entire array is full.
49*/
50volatile uint32 PFS_ALIGNED digest_monotonic_index;
51bool digest_full= false;
52
53LF_HASH digest_hash;
54static bool digest_hash_inited= false;
55
56/**
57 Initialize table EVENTS_STATEMENTS_SUMMARY_BY_DIGEST.
58 @param param performance schema sizing
59*/
60int init_digest(const PFS_global_param *param)
61{
62 /*
63 Allocate memory for statements_digest_stat_array based on
64 performance_schema_digests_size values
65 */
66 digest_max= param->m_digest_sizing;
67 digest_lost= 0;
68 PFS_atomic::store_u32(& digest_monotonic_index, 1);
69 digest_full= false;
70
71 if (digest_max == 0)
72 return 0;
73
74 statements_digest_stat_array=
75 PFS_MALLOC_ARRAY(digest_max,
76 sizeof(PFS_statements_digest_stat),
77 PFS_statements_digest_stat,
78 MYF(MY_ZEROFILL));
79
80 if (unlikely(statements_digest_stat_array == NULL))
81 {
82 cleanup_digest();
83 return 1;
84 }
85
86 if (pfs_max_digest_length > 0)
87 {
88 /* Size of each digest array. */
89 size_t digest_memory_size= pfs_max_digest_length * sizeof(unsigned char);
90
91 statements_digest_token_array=
92 PFS_MALLOC_ARRAY(digest_max,
93 digest_memory_size,
94 unsigned char,
95 MYF(MY_ZEROFILL));
96
97 if (unlikely(statements_digest_token_array == NULL))
98 {
99 cleanup_digest();
100 return 1;
101 }
102 }
103
104 for (size_t index= 0; index < digest_max; index++)
105 {
106 statements_digest_stat_array[index].reset_data(statements_digest_token_array
107 + index * pfs_max_digest_length, pfs_max_digest_length);
108 }
109
110 /* Set record[0] as allocated. */
111 statements_digest_stat_array[0].m_lock.set_allocated();
112
113 return 0;
114}
115
116/** Cleanup table EVENTS_STATEMENTS_SUMMARY_BY_DIGEST. */
117void cleanup_digest(void)
118{
119 /* Free memory allocated to statements_digest_stat_array. */
120 pfs_free(statements_digest_stat_array);
121 pfs_free(statements_digest_token_array);
122 statements_digest_stat_array= NULL;
123 statements_digest_token_array= NULL;
124}
125
126C_MODE_START
127static uchar *digest_hash_get_key(const uchar *entry, size_t *length,
128 my_bool)
129{
130 const PFS_statements_digest_stat * const *typed_entry;
131 const PFS_statements_digest_stat *digest;
132 const void *result;
133 typed_entry= reinterpret_cast<const PFS_statements_digest_stat*const*>(entry);
134 DBUG_ASSERT(typed_entry != NULL);
135 digest= *typed_entry;
136 DBUG_ASSERT(digest != NULL);
137 *length= sizeof (PFS_digest_key);
138 result= & digest->m_digest_key;
139 return const_cast<uchar*> (reinterpret_cast<const uchar*> (result));
140}
141C_MODE_END
142
143
144/**
145 Initialize the digest hash.
146 @return 0 on success
147*/
148int init_digest_hash(void)
149{
150 if ((! digest_hash_inited) && (digest_max > 0))
151 {
152 lf_hash_init(&digest_hash, sizeof(PFS_statements_digest_stat*),
153 LF_HASH_UNIQUE, 0, 0, digest_hash_get_key,
154 &my_charset_bin);
155 /* digest_hash.size= digest_max; */
156 digest_hash_inited= true;
157 }
158 return 0;
159}
160
161void cleanup_digest_hash(void)
162{
163 if (digest_hash_inited)
164 {
165 lf_hash_destroy(&digest_hash);
166 digest_hash_inited= false;
167 }
168}
169
170static LF_PINS* get_digest_hash_pins(PFS_thread *thread)
171{
172 if (unlikely(thread->m_digest_hash_pins == NULL))
173 {
174 if (!digest_hash_inited)
175 return NULL;
176 thread->m_digest_hash_pins= lf_hash_get_pins(&digest_hash);
177 }
178 return thread->m_digest_hash_pins;
179}
180
181PFS_statement_stat*
182find_or_create_digest(PFS_thread *thread,
183 const sql_digest_storage *digest_storage,
184 const char *schema_name,
185 uint schema_name_length)
186{
187 DBUG_ASSERT(digest_storage != NULL);
188
189 if (statements_digest_stat_array == NULL)
190 return NULL;
191
192 if (digest_storage->m_byte_count <= 0)
193 return NULL;
194
195 LF_PINS *pins= get_digest_hash_pins(thread);
196 if (unlikely(pins == NULL))
197 return NULL;
198
199 /*
200 Note: the LF_HASH key is a block of memory,
201 make sure to clean unused bytes,
202 so that memcmp() can compare keys.
203 */
204 PFS_digest_key hash_key;
205 memset(& hash_key, 0, sizeof(hash_key));
206 /* Compute MD5 Hash of the tokens received. */
207 compute_digest_md5(digest_storage, hash_key.m_md5);
208 memcpy((void*)& digest_storage->m_md5, &hash_key.m_md5, MD5_HASH_SIZE);
209 /* Add the current schema to the key */
210 hash_key.m_schema_name_length= schema_name_length;
211 if (schema_name_length > 0)
212 memcpy(hash_key.m_schema_name, schema_name, schema_name_length);
213
214 int res;
215 uint retry_count= 0;
216 const uint retry_max= 3;
217 size_t safe_index;
218 size_t attempts= 0;
219 PFS_statements_digest_stat **entry;
220 PFS_statements_digest_stat *pfs= NULL;
221
222 ulonglong now= my_hrtime().val;
223
224search:
225
226 /* Lookup LF_HASH using this new key. */
227 entry= reinterpret_cast<PFS_statements_digest_stat**>
228 (lf_hash_search(&digest_hash, pins,
229 &hash_key, sizeof(PFS_digest_key)));
230
231 if (entry && (entry != MY_ERRPTR))
232 {
233 /* If digest already exists, update stats and return. */
234 pfs= *entry;
235 pfs->m_last_seen= now;
236 lf_hash_search_unpin(pins);
237 return & pfs->m_stat;
238 }
239
240 lf_hash_search_unpin(pins);
241
242 if (digest_full)
243 {
244 /* digest_stat array is full. Add stat at index 0 and return. */
245 pfs= &statements_digest_stat_array[0];
246 digest_lost++;
247
248 if (pfs->m_first_seen == 0)
249 pfs->m_first_seen= now;
250 pfs->m_last_seen= now;
251 return & pfs->m_stat;
252 }
253
254 while (++attempts <= digest_max)
255 {
256 safe_index= PFS_atomic::add_u32(& digest_monotonic_index, 1) % digest_max;
257 if (safe_index == 0)
258 {
259 /* Record [0] is reserved. */
260 continue;
261 }
262
263 /* Add a new record in digest stat array. */
264 DBUG_ASSERT(safe_index < digest_max);
265 pfs= &statements_digest_stat_array[safe_index];
266
267 if (pfs->m_lock.is_free())
268 {
269 if (pfs->m_lock.free_to_dirty())
270 {
271 /* Copy digest hash/LF Hash search key. */
272 memcpy(& pfs->m_digest_key, &hash_key, sizeof(PFS_digest_key));
273
274 /*
275 Copy digest storage to statement_digest_stat_array so that it could be
276 used later to generate digest text.
277 */
278 pfs->m_digest_storage.copy(digest_storage);
279
280 pfs->m_first_seen= now;
281 pfs->m_last_seen= now;
282
283 res= lf_hash_insert(&digest_hash, pins, &pfs);
284 if (likely(res == 0))
285 {
286 pfs->m_lock.dirty_to_allocated();
287 return & pfs->m_stat;
288 }
289
290 pfs->m_lock.dirty_to_free();
291
292 if (res > 0)
293 {
294 /* Duplicate insert by another thread */
295 if (++retry_count > retry_max)
296 {
297 /* Avoid infinite loops */
298 digest_lost++;
299 return NULL;
300 }
301 goto search;
302 }
303
304 /* OOM in lf_hash_insert */
305 digest_lost++;
306 return NULL;
307 }
308 }
309 }
310
311 /* The digest array is now full. */
312 digest_full= true;
313 pfs= &statements_digest_stat_array[0];
314
315 if (pfs->m_first_seen == 0)
316 pfs->m_first_seen= now;
317 pfs->m_last_seen= now;
318 return & pfs->m_stat;
319}
320
321void purge_digest(PFS_thread* thread, PFS_digest_key *hash_key)
322{
323 LF_PINS *pins= get_digest_hash_pins(thread);
324 if (unlikely(pins == NULL))
325 return;
326
327 PFS_statements_digest_stat **entry;
328
329 /* Lookup LF_HASH using this new key. */
330 entry= reinterpret_cast<PFS_statements_digest_stat**>
331 (lf_hash_search(&digest_hash, pins,
332 hash_key, sizeof(PFS_digest_key)));
333
334 if (entry && (entry != MY_ERRPTR))
335 {
336 lf_hash_delete(&digest_hash, pins,
337 hash_key, sizeof(PFS_digest_key));
338 }
339 lf_hash_search_unpin(pins);
340 return;
341}
342
343void PFS_statements_digest_stat::reset_data(unsigned char *token_array, size_t length)
344{
345 m_lock.set_dirty();
346 m_digest_storage.reset(token_array, length);
347 m_stat.reset();
348 m_first_seen= 0;
349 m_last_seen= 0;
350 m_lock.dirty_to_free();
351}
352
353void PFS_statements_digest_stat::reset_index(PFS_thread *thread)
354{
355 /* Only remove entries that exists in the HASH index. */
356 if (m_digest_storage.m_byte_count > 0)
357 {
358 purge_digest(thread, & m_digest_key);
359 }
360}
361
362void reset_esms_by_digest()
363{
364 if (statements_digest_stat_array == NULL)
365 return;
366
367 PFS_thread *thread= PFS_thread::get_current_thread();
368 if (unlikely(thread == NULL))
369 return;
370
371 /* Reset statements_digest_stat_array. */
372 for (size_t index= 0; index < digest_max; index++)
373 {
374 statements_digest_stat_array[index].reset_index(thread);
375 statements_digest_stat_array[index].reset_data(statements_digest_token_array + index * pfs_max_digest_length, pfs_max_digest_length);
376 }
377
378 /* Mark record[0] as allocated again. */
379 statements_digest_stat_array[0].m_lock.set_allocated();
380
381 /*
382 Reset index which indicates where the next calculated digest information
383 to be inserted in statements_digest_stat_array.
384 */
385 PFS_atomic::store_u32(& digest_monotonic_index, 1);
386 digest_full= false;
387}
388
389