1 /* Copyright (c) 2000, 2010, 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 St, Fifth Floor, Boston, MA 02110-1335  USA */
15 
16 
17 /* Write some debug info */
18 
19 #include "mariadb.h"
20 #include "sql_priv.h"
21 #include "unireg.h"
22 #include "sql_test.h"
23 #include "sql_base.h"
24 #include "sql_show.h" // calc_sum_of_all_status
25 #include "sql_select.h"
26 #include "keycaches.h"
27 #include "my_json_writer.h"
28 #include <hash.h>
29 #include <thr_alarm.h>
30 #include "sql_connect.h"
31 #include "thread_cache.h"
32 #if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H)
33 #include <malloc.h>
34 #elif defined(HAVE_MALLINFO) && defined(HAVE_SYS_MALLOC_H)
35 #include <sys/malloc.h>
36 #elif defined(HAVE_MALLOC_ZONE)
37 #include <malloc/malloc.h>
38 #endif
39 
40 #ifdef HAVE_EVENT_SCHEDULER
41 #include "events.h"
42 #endif
43 
44 #define FT_KEYPART   (MAX_FIELDS+10)
45 
46 static const char *lock_descriptions[] =
47 {
48   /* TL_UNLOCK                  */  "No lock",
49   /* TL_READ_DEFAULT            */  NULL,
50   /* TL_READ                    */  "Low priority read lock",
51   /* TL_READ_WITH_SHARED_LOCKS  */  "Shared read lock",
52   /* TL_READ_HIGH_PRIORITY      */  "High priority read lock",
53   /* TL_READ_NO_INSERT          */  "Read lock without concurrent inserts",
54   /* TL_WRITE_ALLOW_WRITE       */  "Write lock that allows other writers",
55   /* TL_WRITE_CONCURRENT_INSERT */  "Concurrent insert lock",
56   /* TL_WRITE_DELAYED           */  "Lock used by delayed insert",
57   /* TL_WRITE_DEFAULT           */  NULL,
58   /* TL_WRITE_LOW_PRIORITY      */  "Low priority write lock",
59   /* TL_WRITE                   */  "High priority write lock",
60   /* TL_WRITE_ONLY              */  "Highest priority write lock"
61 };
62 
63 
64 #ifndef DBUG_OFF
65 
66 void
print_where(COND * cond,const char * info,enum_query_type query_type)67 print_where(COND *cond,const char *info, enum_query_type query_type)
68 {
69   char buff[1024];
70   String str(buff,(uint32) sizeof(buff), system_charset_info);
71   str.length(0);
72   str.extra_allocation(1024);
73   if (cond)
74     cond->print(&str, query_type);
75 
76   DBUG_LOCK_FILE;
77   (void) fprintf(DBUG_FILE,"\nWHERE:(%s) %p ", info, cond);
78   (void) fputs(str.c_ptr_safe(),DBUG_FILE);
79   (void) fputc('\n',DBUG_FILE);
80   DBUG_UNLOCK_FILE;
81 }
82 
83 #ifdef EXTRA_DEBUG
84 	/* This is for debugging purposes */
print_cached_tables_callback(TDC_element * element,void * arg)85 static my_bool print_cached_tables_callback(TDC_element *element,
86                                             void *arg __attribute__((unused)))
87 {
88   TABLE *entry;
89 
90   mysql_mutex_lock(&element->LOCK_table_share);
91   All_share_tables_list::Iterator it(element->all_tables);
92   while ((entry= it++))
93   {
94     THD *in_use= entry->in_use;
95     printf("%-14.14s %-32s%8ld%6d  %s\n",
96            entry->s->db.str, entry->s->table_name.str,
97            in_use ? (long) in_use->thread_id : (long) 0,
98            entry->db_stat ? 1 : 0,
99            in_use ? lock_descriptions[(int)entry->reginfo.lock_type] :
100                     "Not in use");
101   }
102   mysql_mutex_unlock(&element->LOCK_table_share);
103   return FALSE;
104 }
105 
106 
print_cached_tables(void)107 static void print_cached_tables(void)
108 {
109   compile_time_assert(TL_WRITE_ONLY+1 == array_elements(lock_descriptions));
110 
111   /* purecov: begin tested */
112   puts("DB             Table                            Version  Thread  Open  Lock");
113 
114   tdc_iterate(0, (my_hash_walk_action) print_cached_tables_callback, NULL, true);
115 
116   fflush(stdout);
117   /* purecov: end */
118   return;
119 }
120 #endif
121 
122 
TEST_filesort(SORT_FIELD * sortorder,uint s_length)123 void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
124 {
125   char buff[256],buff2[256];
126   String str(buff,sizeof(buff),system_charset_info);
127   String out(buff2,sizeof(buff2),system_charset_info);
128   const char *sep;
129   DBUG_ENTER("TEST_filesort");
130 
131   out.length(0);
132   for (sep=""; s_length-- ; sortorder++, sep=" ")
133   {
134     out.append(sep);
135     if (sortorder->reverse)
136       out.append('-');
137     if (sortorder->field)
138     {
139       if (sortorder->field->table_name)
140       {
141 	out.append(*sortorder->field->table_name);
142 	out.append('.');
143       }
144       out.append(sortorder->field->field_name.str ?
145                  sortorder->field->field_name.str :
146 		 "tmp_table_column");
147     }
148     else
149     {
150       str.length(0);
151       sortorder->item->print(&str, QT_ORDINARY);
152       out.append(str);
153     }
154   }
155   DBUG_LOCK_FILE;
156   (void) fputs("\nInfo about FILESORT\n",DBUG_FILE);
157   fprintf(DBUG_FILE,"Sortorder: %s\n",out.c_ptr_safe());
158   DBUG_UNLOCK_FILE;
159   DBUG_VOID_RETURN;
160 }
161 
162 
163 void
TEST_join(JOIN * join)164 TEST_join(JOIN *join)
165 {
166   uint ref;
167   int i;
168   List_iterator<JOIN_TAB_RANGE> it(join->join_tab_ranges);
169   JOIN_TAB_RANGE *jt_range;
170   DBUG_ENTER("TEST_join");
171 
172   DBUG_LOCK_FILE;
173   (void) fputs("\nInfo about JOIN\n",DBUG_FILE);
174   while ((jt_range= it++))
175   {
176     /*
177       Assemble results of all the calls to full_name() first,
178       in order not to garble the tabular output below.
179     */
180     String ref_key_parts[MAX_TABLES];
181     int tables_in_range= (int)(jt_range->end - jt_range->start);
182     for (i= 0; i < tables_in_range; i++)
183     {
184       JOIN_TAB *tab= jt_range->start + i;
185       for (ref= 0; ref < tab->ref.key_parts; ref++)
186       {
187         ref_key_parts[i].append(tab->ref.items[ref]->full_name());
188         ref_key_parts[i].append("  ");
189       }
190     }
191 
192     for (i= 0; i < tables_in_range; i++)
193     {
194       JOIN_TAB *tab= jt_range->start + i;
195       TABLE *form=tab->table;
196       char key_map_buff[128];
197       fprintf(DBUG_FILE,"%-16.16s  type: %-7s  q_keys: %s  refs: %d  key: %d  len: %d\n",
198 	    form->alias.c_ptr(),
199               join_type_str[tab->type],
200               tab->keys.print(key_map_buff),
201               tab->ref.key_parts,
202               tab->ref.key,
203               tab->ref.key_length);
204       if (tab->select)
205       {
206         char buf[MAX_KEY/8+1];
207         if (tab->use_quick == 2)
208           fprintf(DBUG_FILE,
209                   "                  quick select checked for each record (keys: %s)\n",
210                   tab->select->quick_keys.print(buf));
211         else if (tab->select->quick)
212         {
213           fprintf(DBUG_FILE, "                  quick select used:\n");
214           tab->select->quick->dbug_dump(18, FALSE);
215         }
216         else
217           (void)fputs("                  select used\n",DBUG_FILE);
218       }
219       if (tab->ref.key_parts)
220       {
221         fprintf(DBUG_FILE,
222               "                  refs:  %s\n", ref_key_parts[i].c_ptr_safe());
223       }
224     }
225     (void)fputs("\n",DBUG_FILE);
226   }
227   DBUG_UNLOCK_FILE;
228   DBUG_VOID_RETURN;
229 }
230 
231 
print_keyuse(KEYUSE * keyuse)232 static void print_keyuse(KEYUSE *keyuse)
233 {
234   char buff[256];
235   char buf2[64];
236   const char *fieldname;
237   JOIN_TAB *join_tab= keyuse->table->reginfo.join_tab;
238   KEY *key_info= join_tab->get_keyinfo_by_key_no(keyuse->key);
239   String str(buff,(uint32) sizeof(buff), system_charset_info);
240   str.length(0);
241   keyuse->val->print(&str, QT_ORDINARY);
242   str.append('\0');
243   if (keyuse->is_for_hash_join())
244     fieldname= keyuse->table->field[keyuse->keypart]->field_name.str;
245   else if (keyuse->keypart == FT_KEYPART)
246     fieldname= "FT_KEYPART";
247   else
248     fieldname= key_info->key_part[keyuse->keypart].field->field_name.str;
249   ll2str(keyuse->used_tables, buf2, 16, 0);
250   fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s  optimize: %u  used_tables: %s "
251           "ref_table_rows: %lu  keypart_map: %0lx\n",
252           keyuse->table->alias.c_ptr(), fieldname, str.ptr(),
253           (uint) keyuse->optimize, buf2, (ulong) keyuse->ref_table_rows,
254           (ulong) keyuse->keypart_map);
255 }
256 
257 
258 /* purecov: begin inspected */
print_keyuse_array(DYNAMIC_ARRAY * keyuse_array)259 void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array)
260 {
261   DBUG_LOCK_FILE;
262   fprintf(DBUG_FILE, "KEYUSE array (%d elements)\n", keyuse_array->elements);
263   for(uint i=0; i < keyuse_array->elements; i++)
264     print_keyuse((KEYUSE*)dynamic_array_ptr(keyuse_array, i));
265   DBUG_UNLOCK_FILE;
266 }
267 
268 /*
269   Print the current state during query optimization.
270 
271   SYNOPSIS
272     print_plan()
273     join         pointer to the structure providing all context info for
274                  the query
275     read_time    the cost of the best partial plan
276     record_count estimate for the number of records returned by the best
277                  partial plan
278     idx          length of the partial QEP in 'join->positions';
279                  also an index in the array 'join->best_ref';
280     info         comment string to appear above the printout
281 
282   DESCRIPTION
283     This function prints to the log file DBUG_FILE the members of 'join' that
284     are used during query optimization (join->positions, join->best_positions,
285     and join->best_ref) and few other related variables (read_time,
286     record_count).
287     Useful to trace query optimizer functions.
288 
289   RETURN
290     None
291 */
292 
293 void
print_plan(JOIN * join,uint idx,double record_count,double read_time,double current_read_time,const char * info)294 print_plan(JOIN* join, uint idx, double record_count, double read_time,
295            double current_read_time, const char *info)
296 {
297   uint i;
298   JOIN_TAB *join_table;
299   JOIN_TAB **plan_nodes;
300   TABLE*   table;
301 
302   if (info == 0)
303     info= "";
304 
305   DBUG_LOCK_FILE;
306   if (join->best_read == DBL_MAX)
307   {
308     fprintf(DBUG_FILE,
309             "%s; idx: %u  best: DBL_MAX  atime: %g  itime: %g  count: %g\n",
310             info, idx, current_read_time, read_time, record_count);
311   }
312   else
313   {
314     fprintf(DBUG_FILE,
315             "%s; idx :%u  best: %g  accumulated: %g  increment: %g  count: %g\n",
316             info, idx, join->best_read, current_read_time, read_time,
317             record_count);
318   }
319 
320   /* Print the tables in JOIN->positions */
321   fputs("     POSITIONS: ", DBUG_FILE);
322   for (i= 0; i < idx ; i++)
323   {
324     POSITION *pos= join->positions + i;
325     table= pos->table->table;
326     if (table)
327       fputs(table->s->table_name.str, DBUG_FILE);
328     fputc(' ', DBUG_FILE);
329   }
330   fputc('\n', DBUG_FILE);
331 
332   /*
333     Print the tables in JOIN->best_positions only if at least one complete plan
334     has been found. An indicator for this is the value of 'join->best_read'.
335   */
336   if (join->best_read < DBL_MAX)
337   {
338     fputs("BEST_POSITIONS: ", DBUG_FILE);
339     for (i= 0; i < idx ; i++)
340     {
341       POSITION *pos= join->best_positions + i;
342       table= pos->table->table;
343       if (table)
344         fputs(table->s->table_name.str, DBUG_FILE);
345       fputc(' ', DBUG_FILE);
346     }
347   }
348   fputc('\n', DBUG_FILE);
349 
350   /* Print the tables in JOIN->best_ref */
351   fputs("      BEST_REF: ", DBUG_FILE);
352   for (plan_nodes= join->best_ref ; *plan_nodes ; plan_nodes++)
353   {
354     join_table= (*plan_nodes);
355     fputs(join_table->table->s->table_name.str, DBUG_FILE);
356     fprintf(DBUG_FILE, "(%lu,%lu,%lu)",
357             (ulong) join_table->found_records,
358             (ulong) join_table->records,
359             (ulong) join_table->read_time);
360     fputc(' ', DBUG_FILE);
361   }
362   fputc('\n', DBUG_FILE);
363 
364   DBUG_UNLOCK_FILE;
365 }
366 
367 
print_sjm(SJ_MATERIALIZATION_INFO * sjm)368 void print_sjm(SJ_MATERIALIZATION_INFO *sjm)
369 {
370   DBUG_LOCK_FILE;
371   fprintf(DBUG_FILE, "\nsemi-join nest{\n");
372   fprintf(DBUG_FILE, "  tables { \n");
373   for (uint i= 0;i < sjm->tables; i++)
374   {
375     fprintf(DBUG_FILE, "    %s%s\n",
376             sjm->positions[i].table->table->alias.c_ptr(),
377             (i == sjm->tables -1)? "": ",");
378   }
379   fprintf(DBUG_FILE, "  }\n");
380   fprintf(DBUG_FILE, "  materialize_cost= %g\n",
381           sjm->materialization_cost.total_cost());
382   fprintf(DBUG_FILE, "  rows= %g\n", sjm->rows);
383   fprintf(DBUG_FILE, "}\n");
384   DBUG_UNLOCK_FILE;
385 }
386 /* purecov: end */
387 
388 /*
389   Debugging help: force List<...>::elem function not be removed as unused.
390 */
391 Item* (List<Item>::*dbug_list_item_elem_ptr)(uint)= &List<Item>::elem;
392 Item_equal* (List<Item_equal>::*dbug_list_item_equal_elem_ptr)(uint)=
393   &List<Item_equal>::elem;
394 TABLE_LIST* (List<TABLE_LIST>::*dbug_list_table_list_elem_ptr)(uint) =
395   &List<TABLE_LIST>::elem;
396 
397 #endif
398 
399 typedef struct st_debug_lock
400 {
401   ulong thread_id;
402   char table_name[FN_REFLEN];
403   bool waiting;
404   const char *lock_text;
405   enum thr_lock_type type;
406 } TABLE_LOCK_INFO;
407 
408 C_MODE_START
dl_compare(const void * p1,const void * p2)409 static int dl_compare(const void *p1, const void *p2)
410 {
411   TABLE_LOCK_INFO *a, *b;
412 
413   a= (TABLE_LOCK_INFO *) p1;
414   b= (TABLE_LOCK_INFO *) p2;
415 
416   if (a->thread_id > b->thread_id)
417     return 1;
418   if (a->thread_id < b->thread_id)
419     return -1;
420   if (a->waiting == b->waiting)
421     return 0;
422   else if (a->waiting)
423     return -1;
424   return 1;
425 }
426 C_MODE_END
427 
428 
push_locks_into_array(DYNAMIC_ARRAY * ar,THR_LOCK_DATA * data,bool wait,const char * text)429 static void push_locks_into_array(DYNAMIC_ARRAY *ar, THR_LOCK_DATA *data,
430 				  bool wait, const char *text)
431 {
432   if (data)
433   {
434     TABLE *table=(TABLE *)data->debug_print_param;
435     if (table && table->s->tmp_table == NO_TMP_TABLE)
436     {
437       TABLE_LOCK_INFO table_lock_info;
438       table_lock_info.thread_id= (ulong)table->in_use->thread_id;
439       memcpy(table_lock_info.table_name, table->s->table_cache_key.str,
440 	     table->s->table_cache_key.length);
441       table_lock_info.table_name[strlen(table_lock_info.table_name)]='.';
442       table_lock_info.waiting=wait;
443       table_lock_info.lock_text=text;
444       // lock_type is also obtainable from THR_LOCK_DATA
445       table_lock_info.type=table->reginfo.lock_type;
446       (void) push_dynamic(ar,(uchar*) &table_lock_info);
447     }
448   }
449 }
450 
451 
452 /*
453   Regarding MERGE tables:
454 
455   For now, the best option is to use the common TABLE *pointer for all
456   cases;  The drawback is that for MERGE tables we will see many locks
457   for the merge tables even if some of them are for individual tables.
458 
459   The way to solve this is to add to 'THR_LOCK' structure a pointer to
460   the filename and use this when printing the data.
461   (We can for now ignore this and just print the same name for all merge
462   table parts;  Please add the above as a comment to the display_lock
463   function so that we can easily add this if we ever need this.
464 */
465 
display_table_locks(void)466 static void display_table_locks(void)
467 {
468   LIST *list;
469   void *saved_base;
470   DYNAMIC_ARRAY saved_table_locks;
471 
472   (void) my_init_dynamic_array(key_memory_locked_thread_list,
473                                &saved_table_locks, sizeof(TABLE_LOCK_INFO),
474                                tc_records() + 20, 50, MYF(0));
475   mysql_mutex_lock(&THR_LOCK_lock);
476   for (list= thr_lock_thread_list; list; list= list_rest(list))
477   {
478     THR_LOCK *lock=(THR_LOCK*) list->data;
479 
480     mysql_mutex_lock(&lock->mutex);
481     push_locks_into_array(&saved_table_locks, lock->write.data, FALSE,
482 			  "Locked - write");
483     push_locks_into_array(&saved_table_locks, lock->write_wait.data, TRUE,
484 			  "Waiting - write");
485     push_locks_into_array(&saved_table_locks, lock->read.data, FALSE,
486 			  "Locked - read");
487     push_locks_into_array(&saved_table_locks, lock->read_wait.data, TRUE,
488 			  "Waiting - read");
489     mysql_mutex_unlock(&lock->mutex);
490   }
491   mysql_mutex_unlock(&THR_LOCK_lock);
492 
493   if (!saved_table_locks.elements)
494     goto end;
495 
496   saved_base= dynamic_element(&saved_table_locks, 0, TABLE_LOCK_INFO *);
497   my_qsort(saved_base, saved_table_locks.elements, sizeof(TABLE_LOCK_INFO),
498            dl_compare);
499   freeze_size(&saved_table_locks);
500 
501   puts("\nThread database.table_name          Locked/Waiting        Lock_type\n");
502 
503   unsigned int i;
504   for (i=0 ; i < saved_table_locks.elements ; i++)
505   {
506     TABLE_LOCK_INFO *dl_ptr=dynamic_element(&saved_table_locks,i,TABLE_LOCK_INFO*);
507     printf("%-8ld%-28.28s%-22s%s\n",
508 	   dl_ptr->thread_id,dl_ptr->table_name,dl_ptr->lock_text,lock_descriptions[(int)dl_ptr->type]);
509   }
510   puts("\n\n");
511 end:
512   delete_dynamic(&saved_table_locks);
513 }
514 
515 C_MODE_START
print_key_cache_status(const char * name,KEY_CACHE * key_cache,void * unused)516 static int print_key_cache_status(const char *name, KEY_CACHE *key_cache,
517                                   void *unused __attribute__((unused)))
518 {
519   char llbuff1[22];
520   char llbuff2[22];
521   char llbuff3[22];
522   char llbuff4[22];
523 
524   if (!key_cache->key_cache_inited)
525   {
526     printf("%s: Not in use\n", name);
527   }
528   else
529   {
530     KEY_CACHE_STATISTICS stats;
531     get_key_cache_statistics(key_cache, 0, &stats);
532 
533     printf("%s\n\
534 Buffer_size:    %10lu\n\
535 Block_size:     %10lu\n\
536 Division_limit: %10lu\n\
537 Age_threshold:  %10lu\n\
538 Partitions:     %10lu\n\
539 blocks used:    %10lu\n\
540 not flushed:    %10lu\n\
541 w_requests:     %10s\n\
542 writes:         %10s\n\
543 r_requests:     %10s\n\
544 reads:          %10s\n\n",
545 	   name,
546 	   (ulong)key_cache->param_buff_size,
547            (ulong)key_cache->param_block_size,
548 	   (ulong)key_cache->param_division_limit,
549            (ulong)key_cache->param_age_threshold,
550            (ulong)key_cache->param_partitions,
551 	   (ulong)stats.blocks_used,
552            (ulong)stats.blocks_changed,
553 	   llstr(stats.write_requests,llbuff1),
554            llstr(stats.writes,llbuff2),
555 	   llstr(stats.read_requests,llbuff3),
556            llstr(stats.reads,llbuff4));
557   }
558   return 0;
559 }
560 C_MODE_END
561 
562 
mysql_print_status()563 void mysql_print_status()
564 {
565   char current_dir[FN_REFLEN];
566   STATUS_VAR tmp;
567   uint count;
568 
569   tmp= global_status_var;
570   count= calc_sum_of_all_status(&tmp);
571   printf("\nStatus information:\n\n");
572   (void) my_getwd(current_dir, sizeof(current_dir),MYF(0));
573   printf("Current dir: %s\n", current_dir);
574   printf("Running threads: %d  Cached threads: %lu  Stack size: %ld\n",
575          count, thread_cache.size(),
576 	 (long) my_thread_stack_size);
577 #ifdef EXTRA_DEBUG
578   thr_print_locks();				// Write some debug info
579   print_cached_tables();
580 #endif
581   /* Print key cache status */
582   puts("\nKey caches:");
583   process_key_caches(print_key_cache_status, 0);
584   printf("\nhandler status:\n\
585 read_key:   %10lu\n\
586 read_next:  %10lu\n\
587 read_rnd    %10lu\n\
588 read_first: %10lu\n\
589 write:      %10lu\n\
590 delete      %10lu\n\
591 update:     %10lu\n",
592 	 tmp.ha_read_key_count,
593 	 tmp.ha_read_next_count,
594 	 tmp.ha_read_rnd_count,
595 	 tmp.ha_read_first_count,
596 	 tmp.ha_write_count,
597 	 tmp.ha_delete_count,
598 	 tmp.ha_update_count);
599   printf("\nTable status:\n\
600 Opened tables: %10lu\n\
601 Open tables:   %10u\n\
602 Open files:    %10u\n\
603 Open streams:  %10lu\n",
604 	 tmp.opened_tables,
605 	 tc_records(),
606 	 my_file_opened,
607 	 my_stream_opened);
608 
609 #ifndef DONT_USE_THR_ALARM
610   ALARM_INFO alarm_info;
611   thr_alarm_info(&alarm_info);
612   printf("\nAlarm status:\n\
613 Active alarms:   %u\n\
614 Max used alarms: %u\n\
615 Next alarm time: %lu\n",
616 	 alarm_info.active_alarms,
617 	 alarm_info.max_used_alarms,
618 	(ulong)alarm_info.next_alarm_time);
619 #endif
620   display_table_locks();
621 #if defined(HAVE_MALLINFO2)
622   struct mallinfo2 info = mallinfo2();
623 #elif defined(HAVE_MALLINFO)
624   struct mallinfo info= mallinfo();
625 #endif
626 #if defined(HAVE_MALLINFO) || defined(HAVE_MALLINFO2)
627   char llbuff[10][22];
628   printf("\nMemory status:\n\
629 Non-mmapped space allocated from system: %s\n\
630 Number of free chunks:                   %lu\n\
631 Number of fastbin blocks:                %lu\n\
632 Number of mmapped regions:               %lu\n\
633 Space in mmapped regions:                %s\n\
634 Maximum total allocated space:           %s\n\
635 Space available in freed fastbin blocks: %s\n\
636 Total allocated space:                   %s\n\
637 Total free space:                        %s\n\
638 Top-most, releasable space:              %s\n\
639 Estimated memory (with thread stack):    %s\n\
640 Global memory allocated by server:       %s\n\
641 Memory allocated by threads:             %s\n",
642 	 llstr(info.arena,   llbuff[0]),
643 	 (ulong) info.ordblks,
644 	 (ulong) info.smblks,
645 	 (ulong) info.hblks,
646 	 llstr(info.hblkhd,   llbuff[1]),
647 	 llstr(info.usmblks,  llbuff[2]),
648 	 llstr(info.fsmblks,  llbuff[3]),
649 	 llstr(info.uordblks, llbuff[4]),
650 	 llstr(info.fordblks, llbuff[5]),
651 	 llstr(info.keepcost, llbuff[6]),
652          llstr((count + thread_cache.size()) * my_thread_stack_size +
653                info.hblkhd + info.arena, llbuff[7]),
654          llstr(tmp.global_memory_used, llbuff[8]),
655          llstr(tmp.local_memory_used, llbuff[9]));
656 
657 #elif defined(HAVE_MALLOC_ZONE)
658   malloc_statistics_t info;
659   char llbuff[4][22];
660 
661   malloc_zone_statistics(nullptr, &info);
662   printf("\nMemory status:\n\
663 Total allocated space:                   %s\n\
664 Total free space:                        %s\n\
665 Global memory allocated by server:       %s\n\
666 Memory allocated by threads:             %s\n",
667          llstr(info.size_allocated, llbuff[0]),
668          llstr((info.size_allocated - info.size_in_use), llbuff[1]),
669          llstr(tmp.global_memory_used, llbuff[2]),
670          llstr(tmp.local_memory_used, llbuff[3]));
671 #endif
672 
673 #ifdef HAVE_EVENT_SCHEDULER
674   Events::dump_internal_status();
675 #endif
676   puts("");
677   fflush(stdout);
678 }
679 
print_keyuse_array_for_trace(THD * thd,DYNAMIC_ARRAY * keyuse_array)680 void print_keyuse_array_for_trace(THD *thd, DYNAMIC_ARRAY *keyuse_array)
681 {
682   DBUG_ASSERT(thd->trace_started());
683 
684   Json_writer_object wrapper(thd);
685   Json_writer_array trace_key_uses(thd, "ref_optimizer_key_uses");
686 
687   for (uint i=0; i < keyuse_array->elements; i++)
688   {
689     KEYUSE *keyuse= (KEYUSE*)dynamic_array_ptr(keyuse_array, i);
690     Json_writer_object keyuse_elem(thd);
691     keyuse_elem.add_table_name(keyuse->table->reginfo.join_tab);
692     keyuse_elem.add("field", (keyuse->keypart == FT_KEYPART) ? "<fulltext>":
693                                         (keyuse->is_for_hash_join() ?
694                                         keyuse->table->field[keyuse->keypart]
695                                                      ->field_name.str :
696                                         keyuse->table->key_info[keyuse->key]
697                                           .key_part[keyuse->keypart]
698                                           .field->field_name.str));
699     keyuse_elem.add("equals",keyuse->val);
700     keyuse_elem.add("null_rejecting",keyuse->null_rejecting);
701   }
702 }
703