1
/* Copyright (C) 2000-2006 MySQL AB
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
/* Write some debug info */
20
#include "mysql_priv.h"
21
#include "sql_select.h"
23
#include <thr_alarm.h>
24
#if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_H)
26
#elif defined(HAVE_MALLINFO) && defined(HAVE_SYS_MALLOC_H)
27
#include <sys/malloc.h>
30
static const char *lock_descriptions[] =
33
"Low priority read lock",
35
"High priority read lock",
36
"Read lock without concurrent inserts",
37
"Write lock that allows other writers",
38
"Write lock, but allow reading",
39
"Concurrent insert lock",
40
"Lock Used by delayed insert",
41
"Low priority write lock",
42
"High priority write lock",
43
"Highest priority write lock"
50
print_where(COND *cond,const char *info, enum_query_type query_type)
55
String str(buff,(uint32) sizeof(buff), system_charset_info);
57
cond->print(&str, query_type);
60
(void) fprintf(DBUG_FILE,"\nWHERE:(%s) ",info);
61
(void) fputs(str.ptr(),DBUG_FILE);
62
(void) fputc('\n',DBUG_FILE);
66
/* This is for debugging purposes */
69
void print_cached_tables(void)
71
uint idx,count,unused;
72
TABLE *start_link,*lnk;
74
/* purecov: begin tested */
75
VOID(pthread_mutex_lock(&LOCK_open));
76
puts("DB Table Version Thread Open Lock");
78
for (idx=unused=0 ; idx < open_cache.records ; idx++)
80
TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
81
printf("%-14.14s %-32s%6ld%8ld%6d %s\n",
82
entry->s->db.str, entry->s->table_name.str, entry->s->version,
83
entry->in_use ? entry->in_use->thread_id : 0L,
84
entry->db_stat ? 1 : 0,
85
entry->in_use ? lock_descriptions[(int)entry->reginfo.lock_type] : "Not in use");
90
if ((start_link=lnk=unused_tables))
94
if (lnk != lnk->next->prev || lnk != lnk->prev->next)
96
printf("unused_links isn't linked properly\n");
99
} while (count++ < open_cache.records && (lnk=lnk->next) != start_link);
100
if (lnk != start_link)
102
printf("Unused_links aren't connected\n");
106
printf("Unused_links (%d) doesn't match open_cache: %d\n", count,unused);
107
printf("\nCurrent refresh version: %ld\n",refresh_version);
108
if (hash_check(&open_cache))
109
printf("Error: File hash table is corrupted\n");
111
VOID(pthread_mutex_unlock(&LOCK_open));
117
void TEST_filesort(SORT_FIELD *sortorder,uint s_length)
119
char buff[256],buff2[256];
120
String str(buff,sizeof(buff),system_charset_info);
121
String out(buff2,sizeof(buff2),system_charset_info);
123
DBUG_ENTER("TEST_filesort");
126
for (sep=""; s_length-- ; sortorder++, sep=" ")
129
if (sortorder->reverse)
131
if (sortorder->field)
133
if (sortorder->field->table_name)
135
out.append(*sortorder->field->table_name);
138
out.append(sortorder->field->field_name ? sortorder->field->field_name:
144
sortorder->item->print(&str, QT_ORDINARY);
148
out.append('\0'); // Purify doesn't like c_ptr()
150
VOID(fputs("\nInfo about FILESORT\n",DBUG_FILE));
151
fprintf(DBUG_FILE,"Sortorder: %s\n",out.ptr());
158
TEST_join(JOIN *join)
161
DBUG_ENTER("TEST_join");
164
VOID(fputs("\nInfo about JOIN\n",DBUG_FILE));
165
for (i=0 ; i < join->tables ; i++)
167
JOIN_TAB *tab=join->join_tab+i;
168
TABLE *form=tab->table;
169
char key_map_buff[128];
170
fprintf(DBUG_FILE,"%-16.16s type: %-7s q_keys: %s refs: %d key: %d len: %d\n",
172
join_type_str[tab->type],
173
tab->keys.print(key_map_buff),
176
tab->ref.key_length);
179
char buf[MAX_KEY/8+1];
180
if (tab->use_quick == 2)
182
" quick select checked for each record (keys: %s)\n",
183
tab->select->quick_keys.print(buf));
184
else if (tab->select->quick)
186
fprintf(DBUG_FILE, " quick select used:\n");
187
tab->select->quick->dbug_dump(18, FALSE);
190
VOID(fputs(" select used\n",DBUG_FILE));
192
if (tab->ref.key_parts)
194
VOID(fputs(" refs: ",DBUG_FILE));
195
for (ref=0 ; ref < tab->ref.key_parts ; ref++)
197
Item *item=tab->ref.items[ref];
198
fprintf(DBUG_FILE,"%s ", item->full_name());
200
VOID(fputc('\n',DBUG_FILE));
207
#define FT_KEYPART (MAX_REF_PARTS+10)
209
void print_keyuse(KEYUSE *keyuse)
213
const char *fieldname;
214
String str(buff,(uint32) sizeof(buff), system_charset_info);
216
keyuse->val->print(&str, QT_ORDINARY);
218
if (keyuse->keypart == FT_KEYPART)
219
fieldname= "FT_KEYPART";
221
fieldname= keyuse->table->key_info[keyuse->key].key_part[keyuse->keypart].field->field_name;
222
longlong2str(keyuse->used_tables, buf2, 16);
224
fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s optimize= %d used_tables=%s "
225
"ref_table_rows= %lu keypart_map= %0lx\n",
226
keyuse->table->alias, fieldname, str.ptr(),
227
keyuse->optimize, buf2, (ulong)keyuse->ref_table_rows,
228
keyuse->keypart_map);
230
//key_part_map keypart_map; --?? there can be several?
234
void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array)
237
fprintf(DBUG_FILE, "KEYUSE array (%d elements)\n", keyuse_array->elements);
239
for(uint i=0; i < keyuse_array->elements; i++)
240
print_keyuse((KEYUSE*)dynamic_array_ptr(keyuse_array, i));
245
Print the current state during query optimization.
249
join pointer to the structure providing all context info for
251
read_time the cost of the best partial plan
252
record_count estimate for the number of records returned by the best
254
idx length of the partial QEP in 'join->positions';
255
also an index in the array 'join->best_ref';
256
info comment string to appear above the printout
259
This function prints to the log file DBUG_FILE the members of 'join' that
260
are used during query optimization (join->positions, join->best_positions,
261
and join->best_ref) and few other related variables (read_time,
263
Useful to trace query optimizer functions.
270
print_plan(JOIN* join, uint idx, double record_count, double read_time,
271
double current_read_time, const char *info)
275
JOIN_TAB *join_table;
276
JOIN_TAB **plan_nodes;
283
if (join->best_read == DBL_MAX)
286
"%s; idx: %u best: DBL_MAX atime: %g itime: %g count: %g\n",
287
info, idx, current_read_time, read_time, record_count);
292
"%s; idx :%u best: %g accumulated: %g increment: %g count: %g\n",
293
info, idx, join->best_read, current_read_time, read_time,
297
/* Print the tables in JOIN->positions */
298
fputs(" POSITIONS: ", DBUG_FILE);
299
for (i= 0; i < idx ; i++)
301
pos = join->positions[i];
302
table= pos.table->table;
304
fputs(table->s->table_name.str, DBUG_FILE);
305
fputc(' ', DBUG_FILE);
307
fputc('\n', DBUG_FILE);
310
Print the tables in JOIN->best_positions only if at least one complete plan
311
has been found. An indicator for this is the value of 'join->best_read'.
313
if (join->best_read < DBL_MAX)
315
fputs("BEST_POSITIONS: ", DBUG_FILE);
316
for (i= 0; i < idx ; i++)
318
pos= join->best_positions[i];
319
table= pos.table->table;
321
fputs(table->s->table_name.str, DBUG_FILE);
322
fputc(' ', DBUG_FILE);
325
fputc('\n', DBUG_FILE);
327
/* Print the tables in JOIN->best_ref */
328
fputs(" BEST_REF: ", DBUG_FILE);
329
for (plan_nodes= join->best_ref ; *plan_nodes ; plan_nodes++)
331
join_table= (*plan_nodes);
332
fputs(join_table->table->s->table_name.str, DBUG_FILE);
333
fprintf(DBUG_FILE, "(%lu,%lu,%lu)",
334
(ulong) join_table->found_records,
335
(ulong) join_table->records,
336
(ulong) join_table->read_time);
337
fputc(' ', DBUG_FILE);
339
fputc('\n', DBUG_FILE);
346
typedef struct st_debug_lock
349
char table_name[FN_REFLEN];
351
const char *lock_text;
352
enum thr_lock_type type;
355
static int dl_compare(TABLE_LOCK_INFO *a,TABLE_LOCK_INFO *b)
357
if (a->thread_id > b->thread_id)
359
if (a->thread_id < b->thread_id)
361
if (a->waiting == b->waiting)
369
static void push_locks_into_array(DYNAMIC_ARRAY *ar, THR_LOCK_DATA *data,
370
bool wait, const char *text)
374
TABLE *table=(TABLE *)data->debug_print_param;
375
if (table && table->s->tmp_table == NO_TMP_TABLE)
377
TABLE_LOCK_INFO table_lock_info;
378
table_lock_info.thread_id= table->in_use->thread_id;
379
memcpy(table_lock_info.table_name, table->s->table_cache_key.str,
380
table->s->table_cache_key.length);
381
table_lock_info.table_name[strlen(table_lock_info.table_name)]='.';
382
table_lock_info.waiting=wait;
383
table_lock_info.lock_text=text;
384
// lock_type is also obtainable from THR_LOCK_DATA
385
table_lock_info.type=table->reginfo.lock_type;
386
VOID(push_dynamic(ar,(uchar*) &table_lock_info));
393
Regarding MERGE tables:
395
For now, the best option is to use the common TABLE *pointer for all
396
cases; The drawback is that for MERGE tables we will see many locks
397
for the merge tables even if some of them are for individual tables.
399
The way to solve this is to add to 'THR_LOCK' structure a pointer to
400
the filename and use this when printing the data.
401
(We can for now ignore this and just print the same name for all merge
402
table parts; Please add the above as a comment to the display_lock
403
function so that we can easily add this if we ever need this.
406
static void display_table_locks(void)
409
DYNAMIC_ARRAY saved_table_locks;
411
VOID(my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO),open_cache.records + 20,50));
412
VOID(pthread_mutex_lock(&THR_LOCK_lock));
413
for (list= thr_lock_thread_list; list; list= list_rest(list))
415
THR_LOCK *lock=(THR_LOCK*) list->data;
417
VOID(pthread_mutex_lock(&lock->mutex));
418
push_locks_into_array(&saved_table_locks, lock->write.data, FALSE,
420
push_locks_into_array(&saved_table_locks, lock->write_wait.data, TRUE,
422
push_locks_into_array(&saved_table_locks, lock->read.data, FALSE,
424
push_locks_into_array(&saved_table_locks, lock->read_wait.data, TRUE,
426
VOID(pthread_mutex_unlock(&lock->mutex));
428
VOID(pthread_mutex_unlock(&THR_LOCK_lock));
429
if (!saved_table_locks.elements) goto end;
431
qsort((uchar*) dynamic_element(&saved_table_locks,0,TABLE_LOCK_INFO *),saved_table_locks.elements,sizeof(TABLE_LOCK_INFO),(qsort_cmp) dl_compare);
432
freeze_size(&saved_table_locks);
434
puts("\nThread database.table_name Locked/Waiting Lock_type\n");
437
for (i=0 ; i < saved_table_locks.elements ; i++)
439
TABLE_LOCK_INFO *dl_ptr=dynamic_element(&saved_table_locks,i,TABLE_LOCK_INFO*);
440
printf("%-8ld%-28.28s%-22s%s\n",
441
dl_ptr->thread_id,dl_ptr->table_name,dl_ptr->lock_text,lock_descriptions[(int)dl_ptr->type]);
445
delete_dynamic(&saved_table_locks);
449
static int print_key_cache_status(const char *name, KEY_CACHE *key_cache)
456
if (!key_cache->key_cache_inited)
458
printf("%s: Not in use\n", name);
463
Buffer_size: %10lu\n\
465
Division_limit: %10lu\n\
467
blocks used: %10lu\n\
468
not flushed: %10lu\n\
474
(ulong) key_cache->param_buff_size, key_cache->param_block_size,
475
key_cache->param_division_limit, key_cache->param_age_threshold,
476
key_cache->blocks_used,key_cache->global_blocks_changed,
477
llstr(key_cache->global_cache_w_requests,llbuff1),
478
llstr(key_cache->global_cache_write,llbuff2),
479
llstr(key_cache->global_cache_r_requests,llbuff3),
480
llstr(key_cache->global_cache_read,llbuff4));
486
void mysql_print_status()
488
char current_dir[FN_REFLEN];
491
calc_sum_of_all_status(&tmp);
492
printf("\nStatus information:\n\n");
493
VOID(my_getwd(current_dir, sizeof(current_dir),MYF(0)));
494
printf("Current dir: %s\n", current_dir);
495
printf("Running threads: %d Stack size: %ld\n", thread_count,
496
(long) my_thread_stack_size);
497
thr_print_locks(); // Write some debug info
499
print_cached_tables();
501
/* Print key cache status */
502
puts("\nKey caches:");
503
process_key_caches(print_key_cache_status);
504
pthread_mutex_lock(&LOCK_status);
505
printf("\nhandler status:\n\
513
tmp.ha_read_key_count,
514
tmp.ha_read_next_count,
515
tmp.ha_read_rnd_count,
516
tmp.ha_read_first_count,
519
tmp.ha_update_count);
520
pthread_mutex_unlock(&LOCK_status);
521
printf("\nTable status:\n\
522
Opened tables: %10lu\n\
523
Open tables: %10lu\n\
525
Open streams: %10lu\n",
527
(ulong) cached_open_tables(),
528
(ulong) my_file_opened,
529
(ulong) my_stream_opened);
531
ALARM_INFO alarm_info;
532
#ifndef DONT_USE_THR_ALARM
533
thr_alarm_info(&alarm_info);
534
printf("\nAlarm status:\n\
536
Max used alarms: %u\n\
537
Next alarm time: %lu\n",
538
alarm_info.active_alarms,
539
alarm_info.max_used_alarms,
540
alarm_info.next_alarm_time);
542
display_table_locks();
545
fprintf(stdout,"\nBegin safemalloc memory dump:\n"); // tag needed for test suite
546
TERMINATE(stdout, 1); // Write malloc information
547
fprintf(stdout,"\nEnd safemalloc memory dump.\n");
550
struct mallinfo info= mallinfo();
551
printf("\nMemory status:\n\
552
Non-mmapped space allocated from system: %d\n\
553
Number of free chunks: %d\n\
554
Number of fastbin blocks: %d\n\
555
Number of mmapped regions: %d\n\
556
Space in mmapped regions: %d\n\
557
Maximum total allocated space: %d\n\
558
Space available in freed fastbin blocks: %d\n\
559
Total allocated space: %d\n\
560
Total free space: %d\n\
561
Top-most, releasable space: %d\n\
562
Estimated memory (with thread stack): %ld\n",
573
(long) (thread_count * my_thread_stack_size + info.hblkhd + info.arena));
581
#ifdef EXTRA_DEBUG_DUMP_TABLE_LISTS
585
A fixed-size FIFO pointer queue that also doesn't allow one to put an
586
element that has previously been put into it.
588
There is a hard-coded limit of the total number of queue put operations.
589
The implementation is trivial and is intended for use in debug dumps only.
592
template <class T> class Unique_fifo_queue
595
/* Add an element to the queue */
596
void push_back(T *tbl)
600
// check if we've already scheduled and/or dumped the element
601
for (int i= 0; i < last; i++)
609
bool pop_first(T **elem)
613
*elem= elems[first++];
623
enum { MAX_ELEMS=1000};
625
int first; // First undumped table
626
int last; // Last undumped element
629
class Dbug_table_list_dumper
632
Unique_fifo_queue<TABLE_LIST> tables_fifo;
633
Unique_fifo_queue<List<TABLE_LIST> > tbl_lists;
635
void dump_one_struct(TABLE_LIST *tbl);
637
int dump_graph(st_select_lex *select_lex, TABLE_LIST *first_leaf);
641
void dump_TABLE_LIST_graph(SELECT_LEX *select_lex, TABLE_LIST* tl)
643
Dbug_table_list_dumper dumper;
644
dumper.dump_graph(select_lex, tl);
649
- Dump one TABLE_LIST objects and its outgoing edges
650
- Schedule that other objects seen along the edges are dumped too.
653
void Dbug_table_list_dumper::dump_one_struct(TABLE_LIST *tbl)
655
fprintf(out, "\"%p\" [\n", tbl);
656
fprintf(out, " label = \"%p|", tbl);
657
fprintf(out, "alias=%s|", tbl->alias? tbl->alias : "NULL");
658
fprintf(out, "<next_leaf>next_leaf=%p|", tbl->next_leaf);
659
fprintf(out, "<next_local>next_local=%p|", tbl->next_local);
660
fprintf(out, "<next_global>next_global=%p|", tbl->next_global);
661
fprintf(out, "<embedding>embedding=%p", tbl->embedding);
663
if (tbl->nested_join)
664
fprintf(out, "|<nested_j>nested_j=%p", tbl->nested_join);
666
fprintf(out, "|<join_list>join_list=%p", tbl->join_list);
668
fprintf(out, "|<on_expr>on_expr=%p", tbl->on_expr);
669
fprintf(out, "\"\n");
670
fprintf(out, " shape = \"record\"\n];\n\n");
674
fprintf(out, "\n\"%p\":next_leaf -> \"%p\"[ color = \"#000000\" ];\n",
675
tbl, tbl->next_leaf);
676
tables_fifo.push_back(tbl->next_leaf);
680
fprintf(out, "\n\"%p\":next_local -> \"%p\"[ color = \"#404040\" ];\n",
681
tbl, tbl->next_local);
682
tables_fifo.push_back(tbl->next_local);
684
if (tbl->next_global)
686
fprintf(out, "\n\"%p\":next_global -> \"%p\"[ color = \"#808080\" ];\n",
687
tbl, tbl->next_global);
688
tables_fifo.push_back(tbl->next_global);
693
fprintf(out, "\n\"%p\":embedding -> \"%p\"[ color = \"#FF0000\" ];\n",
694
tbl, tbl->embedding);
695
tables_fifo.push_back(tbl->embedding);
700
fprintf(out, "\n\"%p\":join_list -> \"%p\"[ color = \"#0000FF\" ];\n",
701
tbl, tbl->join_list);
702
tbl_lists.push_back(tbl->join_list);
707
int Dbug_table_list_dumper::dump_graph(st_select_lex *select_lex,
708
TABLE_LIST *first_leaf)
710
DBUG_ENTER("Dbug_table_list_dumper::dump_graph");
715
sprintf(filename, "tlist_tree%.3d.g", no);
716
if ((out= fopen(filename, "rt")))
718
/* File exists, try next name */
724
/* Ok, found an unoccupied name, create the file */
725
if (!(out= fopen(filename, "wt")))
727
DBUG_PRINT("tree_dump", ("Failed to create output file"));
731
DBUG_PRINT("tree_dump", ("dumping tree to %s", filename));
733
fputs("digraph g {\n", out);
734
fputs("graph [", out);
735
fputs(" rankdir = \"LR\"", out);
740
dump_one_struct(first_leaf);
741
while (tables_fifo.pop_first(&tbl))
743
dump_one_struct(tbl);
746
List<TABLE_LIST> *plist;
747
tbl_lists.push_back(&select_lex->top_join_list);
748
while (tbl_lists.pop_first(&plist))
750
fprintf(out, "\"%p\" [\n", plist);
751
fprintf(out, " bgcolor = \"\"");
752
fprintf(out, " label = \"L %p\"", plist);
753
fprintf(out, " shape = \"record\"\n];\n\n");
756
fprintf(out, " { rank = same; ");
757
for (TABLE_LIST *tl=first_leaf; tl; tl= tl->next_leaf)
758
fprintf(out, " \"%p\"; ", tl);
759
fprintf(out, "};\n");
764
filename[strlen(filename) - 1]= 0;
765
filename[strlen(filename) - 1]= 0;
766
sprintf(filename2, "%s.query", filename);
768
if ((out= fopen(filename2, "wt")))
770
fprintf(out, "%s", current_thd->query);