From 9c435716a9773f78cd5db0a992bd4b89b83bc25a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 15 Mar 2002 18:44:44 -0700 Subject: replication updates and bugfixes. Still not perfect - there is some strange memory corruption I can only repeat on one system. mysql-test/r/multi_update.result: extended test trying to find why the old one was failing mysql-test/r/rpl000015.result: result update mysql-test/t/multi_update.test: updated test to do more intermediate result checks mysql-test/t/rpl000014.test: cosmetic change mysql-test/t/rpl000015-slave-master-info.opt: reduce connect retry trying to simulate a bug mysql-test/t/rpl000015.test: cosmetic change sql/lex.h: added RELAY_LOG_FILE and RELAY_LOG_POS to CHANGE MASTER TO sql/log.cc: fixed replication bug sql/log_event.cc: properly ignore replication errors according to error mask sql/mysqld.cc: handle SIGFPE in the coredump handler sql/slave.cc: do not kick slave if it has already exited and freed the structures sql/sql_lex.cc: fixed initialization bug in CHANGE MASTER TO sql/sql_lex.h: CHANGE MASTER TO .. RELAY_LOG_FILE=,RELAY_LOG_POS= sql/sql_repl.cc: CHANGE MASTER TO .. RELAY_LOG_FILE=,RELAY_LOG_POS= sql/sql_yacc.yy: CHANGE MASTER TO .. RELAY_LOG_FILE=,RELAY_LOG_POS= --- mysql-test/r/multi_update.result | 27 ++++++++++++++++ mysql-test/r/rpl000015.result | 6 ++-- mysql-test/t/multi_update.test | 11 +++++++ mysql-test/t/rpl000014.test | 1 + mysql-test/t/rpl000015-slave-master-info.opt | 2 +- mysql-test/t/rpl000015.test | 1 + sql/lex.h | 2 ++ sql/log.cc | 1 + sql/log_event.cc | 3 +- sql/mysqld.cc | 1 + sql/slave.cc | 10 +++++- sql/sql_lex.cc | 1 + sql/sql_lex.h | 2 ++ sql/sql_repl.cc | 47 +++++++++++++++++++++++----- sql/sql_yacc.yy | 16 ++++++++++ 15 files changed, 117 insertions(+), 14 deletions(-) diff --git a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result index b1423667c44..85819d9571d 100644 --- a/mysql-test/r/multi_update.result +++ b/mysql-test/r/multi_update.result @@ -2,7 +2,34 @@ drop table if exists t1,t2,t3; create table t1(id1 int not null auto_increment primary key, t char(12)); create table t2(id2 int not null, t char(12)); create table t3(id3 int not null, t char(12), index(id3)); +select count(*) from t1 where id1 > 95; +count(*) +5 +select count(*) from t2 where id2 > 95; +count(*) +25 +select count(*) from t3 where id3 > 95; +count(*) +250 update t1,t2,t3 set t1.t="aaa", t2.t="bbb", t3.t="cc" where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 90; +select count(*) from t1 where t = "aaa"; +count(*) +10 +select count(*) from t1 where id1 > 90; +count(*) +10 +select count(*) from t2 where t = "bbb"; +count(*) +10 +select count(*) from t2 where id2 > 90; +count(*) +50 +select count(*) from t3 where t = "cc"; +count(*) +500 +select count(*) from t3 where id3 > 90; +count(*) +500 delete t1.*, t2.*, t3.* from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 95; check table t1, t2, t3; Table Op Msg_type Msg_text diff --git a/mysql-test/r/rpl000015.result b/mysql-test/r/rpl000015.result index 03370bc6b0d..1546e5eb48d 100644 --- a/mysql-test/r/rpl000015.result +++ b/mysql-test/r/rpl000015.result @@ -9,16 +9,16 @@ Master_Host Master_User Master_Port Connect_retry Master_Log_File Read_Master_Lo change master to master_host='127.0.0.1'; show slave status; Master_Host Master_User Master_Port Connect_retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_do_db Replicate_ignore_db Last_errno Last_error Skip_counter Exec_master_log_pos -127.0.0.1 test MASTER_PORT 60 4 slave-relay-bin.001 4 No No 0 0 0 +127.0.0.1 test MASTER_PORT 7 4 slave-relay-bin.001 4 No No 0 0 0 change master to master_host='127.0.0.1',master_user='root', master_password='',master_port=MASTER_PORT; show slave status; Master_Host Master_User Master_Port Connect_retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_do_db Replicate_ignore_db Last_errno Last_error Skip_counter Exec_master_log_pos -127.0.0.1 root MASTER_PORT 60 4 slave-relay-bin.001 4 No No 0 0 0 +127.0.0.1 root MASTER_PORT 7 4 slave-relay-bin.001 4 No No 0 0 0 slave start; show slave status; Master_Host Master_User Master_Port Connect_retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_do_db Replicate_ignore_db Last_errno Last_error Skip_counter Exec_master_log_pos -127.0.0.1 root MASTER_PORT 60 master-bin.001 79 slave-relay-bin.001 120 master-bin.001 Yes Yes 0 0 79 +127.0.0.1 root MASTER_PORT 7 master-bin.001 79 slave-relay-bin.001 120 master-bin.001 Yes Yes 0 0 79 drop table if exists t1; create table t1 (n int); insert into t1 values (10),(45),(90); diff --git a/mysql-test/t/multi_update.test b/mysql-test/t/multi_update.test index 11697f794a9..c16189168d2 100644 --- a/mysql-test/t/multi_update.test +++ b/mysql-test/t/multi_update.test @@ -29,7 +29,18 @@ while ($1) dec $1; } enable_query_log; + +select count(*) from t1 where id1 > 95; +select count(*) from t2 where id2 > 95; +select count(*) from t3 where id3 > 95; + update t1,t2,t3 set t1.t="aaa", t2.t="bbb", t3.t="cc" where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 90; +select count(*) from t1 where t = "aaa"; +select count(*) from t1 where id1 > 90; +select count(*) from t2 where t = "bbb"; +select count(*) from t2 where id2 > 90; +select count(*) from t3 where t = "cc"; +select count(*) from t3 where id3 > 90; delete t1.*, t2.*, t3.* from t1,t2,t3 where t1.id1 = t2.id2 and t2.id2 = t3.id3 and t1.id1 > 95; check table t1, t2, t3; diff --git a/mysql-test/t/rpl000014.test b/mysql-test/t/rpl000014.test index a673a23dde2..df15fa6e600 100644 --- a/mysql-test/t/rpl000014.test +++ b/mysql-test/t/rpl000014.test @@ -8,6 +8,7 @@ sync_with_master; show slave status; change master to master_log_pos=73; slave stop; + change master to master_log_pos=73; --replace_result 3306 MASTER_PORT 9306 MASTER_PORT 3334 MASTER_PORT 3336 MASTER_PORT show slave status; diff --git a/mysql-test/t/rpl000015-slave-master-info.opt b/mysql-test/t/rpl000015-slave-master-info.opt index 80190bf6d29..28bc753dd56 100644 --- a/mysql-test/t/rpl000015-slave-master-info.opt +++ b/mysql-test/t/rpl000015-slave-master-info.opt @@ -1 +1 @@ ---server-id=2 +--server-id=22 --master-connect-retry=7 diff --git a/mysql-test/t/rpl000015.test b/mysql-test/t/rpl000015.test index 8fd04613ab8..c42e14699c5 100644 --- a/mysql-test/t/rpl000015.test +++ b/mysql-test/t/rpl000015.test @@ -8,6 +8,7 @@ connection slave; reset slave; --replace_result $MASTER_MYPORT MASTER_PORT show slave status; + change master to master_host='127.0.0.1'; # The following needs to be cleaned up when change master is fixed --replace_result $MASTER_MYPORT MASTER_PORT 3306 MASTER_PORT 3334 MASTER_PORT diff --git a/sql/lex.h b/sql/lex.h index 62540c82c28..8c0e838a1b0 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -280,6 +280,8 @@ static SYMBOL symbols[] = { { "READ", SYM(READ_SYM),0,0}, { "REAL", SYM(REAL),0,0}, { "REFERENCES", SYM(REFERENCES),0,0}, + { "RELAY_LOG_FILE", SYM(RELAY_LOG_FILE_SYM),0,0}, + { "RELAY_LOG_POS", SYM(RELAY_LOG_POS_SYM),0,0}, { "RELOAD", SYM(RELOAD),0,0}, { "REGEXP", SYM(REGEXP),0,0}, { "RENAME", SYM(RENAME),0,0}, diff --git a/sql/log.cc b/sql/log.cc index 1052b3379b3..dc6b1d35cef 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -479,6 +479,7 @@ err: rli->relay_log_pos = 4; strnmov(rli->relay_log_name,rli->linfo.log_file_name, sizeof(rli->relay_log_name)); + flush_relay_log_info(rli); } /* No need to free io_buf because we allocated both fname and io_buf in diff --git a/sql/log_event.cc b/sql/log_event.cc index 528110deb74..8db10a84661 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1607,7 +1607,8 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli) { mysql_parse(thd, thd->query, q_len); if (expected_error != - (actual_error = thd->net.last_errno) && expected_error) + (actual_error = thd->net.last_errno) && expected_error && + !ignored_error_code(actual_error)) { const char* errmsg = "Slave: did not get the expected error\ running query from master - expected: '%s' (%d), got '%s' (%d)"; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 46eda3a1e2f..da93a481a5a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1377,6 +1377,7 @@ static void init_signals(void) sigaction(SIGBUS, &sa, NULL); #endif sigaction(SIGILL, &sa, NULL); + sigaction(SIGFPE, &sa, NULL); } (void) sigemptyset(&set); #ifdef THREAD_SPECIFIC_SIGPIPE diff --git a/sql/slave.cc b/sql/slave.cc index 0ffc37eb975..1d701ce1f81 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -345,7 +345,13 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, } } DBUG_ASSERT(thd != 0); - KICK_SLAVE(thd); + /* is is criticate to test if the slave is running. Otherwise, we might + be referening freed memory trying to kick it + */ + if (*slave_running) + { + KICK_SLAVE(thd); + } while (*slave_running) { /* there is a small chance that slave thread might miss the first @@ -367,7 +373,9 @@ int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, DBUG_ASSERT_LOCK(cond_lock); pthread_cond_timedwait(term_cond, cond_lock, &abstime); if (*slave_running) + { KICK_SLAVE(thd); + } } if (term_lock) pthread_mutex_unlock(term_lock); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 43195bc908b..42a8a700da3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -151,6 +151,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length) lex->yacc_yyss=lex->yacc_yyvs=0; lex->ignore_space=test(thd->sql_mode & MODE_IGNORE_SPACE); lex->slave_thd_opt=0; + bzero(&lex->mi,sizeof(lex->mi)); return lex; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ecfb3dec99c..6961ab8c712 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -98,6 +98,8 @@ typedef struct st_lex_master_info uint port, connect_retry; ulonglong pos; ulong server_id; + char* relay_log_name; + ulong relay_log_pos; } LEX_MASTER_INFO; diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index c6384817512..bbe92f3e526 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -690,6 +690,7 @@ int change_master(THD* thd, MASTER_INFO* mi) { int error=0,restart_thread_mask; const char* errmsg=0; + bool need_relay_log_purge=1; // kill slave thread lock_slave_threads(mi); @@ -742,17 +743,47 @@ int change_master(THD* thd, MASTER_INFO* mi) if (lex_mi->connect_retry) mi->connect_retry = lex_mi->connect_retry; + if (lex_mi->relay_log_name) + { + need_relay_log_purge = 0; + strnmov(mi->rli.relay_log_name,lex_mi->relay_log_name, + sizeof(mi->rli.relay_log_name)); + } + + if (lex_mi->relay_log_pos) + { + need_relay_log_purge=0; + mi->rli.relay_log_pos=lex_mi->relay_log_pos; + } + flush_master_info(mi); - pthread_mutex_unlock(&mi->data_lock); - thd->proc_info="purging old relay logs"; - if (purge_relay_logs(&mi->rli,0 /* not only reset, but also reinit*/, - &errmsg)) + if (need_relay_log_purge) { - send_error(&thd->net, 0, "Failed purging old relay logs"); - unlock_slave_threads(mi); - return 1; + pthread_mutex_unlock(&mi->data_lock); + thd->proc_info="purging old relay logs"; + if (purge_relay_logs(&mi->rli,0 /* not only reset, but also reinit*/, + &errmsg)) + { + send_error(&thd->net, 0, "Failed purging old relay logs"); + unlock_slave_threads(mi); + return 1; + } + pthread_mutex_lock(&mi->rli.data_lock); + } + else + { + const char* msg; + if (init_relay_log_pos(&mi->rli,0/*log already inited*/, + 0 /*pos already inited*/, + 0 /*no data lock*/, + &msg)) + { + net_printf(&thd->net,0,"Failed initializing relay log position: %s",msg); + unlock_slave_threads(mi); + return 1; + } + } - pthread_mutex_lock(&mi->rli.data_lock); mi->rli.master_log_pos = mi->master_log_pos; strnmov(mi->rli.master_log_name,mi->master_log_name, sizeof(mi->rli.master_log_name)); diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 850a8ad7ee8..3336a5f71fc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -241,6 +241,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MASTER_PORT_SYM %token MASTER_CONNECT_RETRY_SYM %token MASTER_SERVER_ID_SYM +%token RELAY_LOG_FILE_SYM +%token RELAY_LOG_POS_SYM %token MATCH %token MAX_ROWS %token MAX_QUERIES_PER_HOUR @@ -700,6 +702,16 @@ master_def: { Lex->mi.connect_retry = $3; } + | + RELAY_LOG_FILE_SYM EQ TEXT_STRING + { + Lex->mi.relay_log_name = $3.str; + } + | + RELAY_LOG_POS_SYM EQ ULONG_NUM + { + Lex->mi.relay_log_pos = $3; + } /* create a table */ @@ -3002,6 +3014,7 @@ keyword: | ISSUER_SYM {} | INNOBASE_SYM {} | INSERT_METHOD {} + | IO_THREAD {} | LAST_SYM {} | LEVEL_SYM {} | LOCAL_SYM {} @@ -3044,6 +3057,8 @@ keyword: | RAID_CHUNKSIZE {} | RAID_STRIPED_SYM {} | RAID_TYPE {} + | RELAY_LOG_FILE_SYM {} + | RELAY_LOG_POS_SYM {} | RELOAD {} | REPAIR {} | REPEATABLE_SYM {} @@ -3063,6 +3078,7 @@ keyword: | SQL_CACHE_SYM {} | SQL_NO_CACHE_SYM {} | SQL_QUERY_CACHE_TYPE_SYM {} + | SQL_THREAD {} | START_SYM {} | STATUS_SYM {} | STOP_SYM {} -- cgit v1.2.1 From 079a551ca735e79f3d7a2ebbecf8be4d4ba7608a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 26 Mar 2002 22:19:23 -0700 Subject: coverted my_thread_init() /end to use my_malloc()/my_free() to help track down replication corruption dbug/dbug.c: fixed DBUG_ macros to not crash if my_thread_init() had not been called include/my_sys.h: keep track of shutdown, main, and signal threads for safemalloc mysys/my_static.h: keep track of thread in safemalloc blocks mysys/my_thr_init.c: use my_malloc()/my_free() in my_thread_init() mysys/safemalloc.c: keep track of threads do not fuss if main,signal, or shutdown thread still has allocated blocks when we call TERMINATE() as some blocks are freed later and there is no easy way to make TERMINATE() wait for those threads sql/mysqld.cc: keep track of signal, main, and shutdown threads --- dbug/dbug.c | 28 ++++++++++++++++----- include/my_sys.h | 4 +++ mysys/my_static.h | 6 +++++ mysys/my_thr_init.c | 39 +++++++++++++++++++++++++---- mysys/safemalloc.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++- sql/mysqld.cc | 23 ++++++++++++++++-- 6 files changed, 156 insertions(+), 14 deletions(-) diff --git a/dbug/dbug.c b/dbug/dbug.c index 9e1e51d2404..8f214bfbe7c 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -21,7 +21,8 @@ * all copies and derivative works. Thank you. * * * * The author makes no warranty of any kind with respect to this * - * product and explicitly disclaims any implied warranties of mer- * + * product and explicitly disclaims any implied warranties of mer- *ct_lex.table_list.first=0; + thd->lex.selec * chantability or fitness for any particular purpose. * * * ****************************************************************************** @@ -58,7 +59,7 @@ * seismo!bpa!sjuvax!bbanerje * * Michael Widenius: - * DBUG_DUMP - To dump a pice of memory. + * DBUG_DUMP - To dump a block of memory. * PUSH_FLAG "O" - To be used insted of "o" if we don't * want flushing (for slow systems) * PUSH_FLAG "A" - as 'O', but we will append to the out file instead @@ -707,7 +708,13 @@ char ***_sframep_ __attribute__((unused))) int save_errno=errno; if (!init_done) _db_push_ (_DBUG_START_CONDITION_); - state=code_state(); + /* Sasha: the test below is so we could call functions with DBUG_ENTER + before my_thread_init(). I needed this because I suspected corruption + of a block allocated by my_thread_init() itself, so I wanted to use + my_malloc()/my_free() in my_thread_init()/my_thread_end() + */ + if (!(state=code_state())) + return; *_sfunc_ = state->func; *_sfile_ = state->file; @@ -855,6 +862,9 @@ uint _line_, const char *keyword) { CODE_STATE *state=code_state(); + /* Sasha: pre-my_thread_init() safety */ + if (!state) + return; state->u_line = _line_; state->u_keyword = (char*) keyword; } @@ -890,7 +900,9 @@ void _db_doprnt_ (const char *format,...) { va_list args; CODE_STATE *state; - state=code_state(); + /* Sasha: pre-my_thread_init() safety */ + if (!(state=code_state())) + return; va_start(args,format); @@ -942,7 +954,9 @@ uint length) int pos; char dbuff[90]; CODE_STATE *state; - state=code_state(); + /* Sasha: pre-my_thread_init() safety */ + if (!(state=code_state())) + return; if (_db_keyword_ ((char*) keyword)) { @@ -1224,7 +1238,9 @@ const char *keyword) if (!init_done) _db_push_ (""); - state=code_state(); + /* Sasha: pre-my_thread_init() safety */ + if (!(state=code_state())) + return FALSE; result = FALSE; if (DEBUGGING && state->level <= stack -> maxdepth && diff --git a/include/my_sys.h b/include/my_sys.h index 4aee27e2939..3950bfce758 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -137,6 +137,10 @@ extern int NEAR my_errno; /* Last error in mysys */ #define NORMAL_SAFEMALLOC sf_malloc_quick=0 extern uint sf_malloc_prehunc,sf_malloc_endhunc,sf_malloc_quick; extern ulonglong safemalloc_mem_limit; +/* keep track of shutdown,signal, and main threads so that my_end() will not + report errors with them +*/ +extern pthread_t shutdown_th, main_th,signal_th; #define CALLER_INFO_PROTO , const char *sFile, uint uLine #define CALLER_INFO , __FILE__, __LINE__ #define ORIG_CALLER_INFO , sFile, uLine diff --git a/mysys/my_static.h b/mysys/my_static.h index 4f944938b8d..ca384009063 100644 --- a/mysys/my_static.h +++ b/mysys/my_static.h @@ -38,6 +38,7 @@ struct irem { my_string _sFileName; /* File in which memory was new'ed */ uint _uLineNum; /* Line number in above file */ uint _uDataSize; /* Size requested */ + pthread_t thread_id; long _lSpecialValue; /* Underrun marker value */ }; @@ -56,6 +57,11 @@ extern const char *soundex_map; extern USED_MEM* my_once_root_block; extern uint my_once_extra; +/* these threads are exept from safemalloc leak scrutiny unless + PEDANTIC_SAFEMALLOC is defined +*/ +extern pthread_t signal_thread,kill_thread; + #ifndef HAVE_TEMPNAM extern int _my_tempnam_used; #endif diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index b4d98feeeb6..2c7fd098c63 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -105,19 +105,33 @@ static long thread_id=0; my_bool my_thread_init(void) { struct st_my_thread_var *tmp; +#ifdef EXTRA_DEBUG + fprintf(stderr,"my_thread_init(): thread_id=%ld\n",pthread_self()); +#endif #if !defined(__WIN__) || defined(USE_TLS) || ! defined(SAFE_MUTEX) pthread_mutex_lock(&THR_LOCK_lock); #endif #if !defined(__WIN__) || defined(USE_TLS) if (my_pthread_getspecific(struct st_my_thread_var *,THR_KEY_mysys)) { +#ifdef EXTRA_DEBUG + fprintf(stderr,"my_thread_init() called more than once in thread %ld\n", + pthread_self()); +#endif pthread_mutex_unlock(&THR_LOCK_lock); return 0; /* Safequard */ } /* We must have many calloc() here because these are freed on pthread_exit */ + /* + Sasha: the above comment does not make sense. I have changed calloc() to + equivalent my_malloc() but it was calloc() before. It seems like the + comment is out of date - we always call my_thread_end() before + pthread_exit() to clean up. Note that I have also fixed up DBUG + code to be able to call it from my_thread_init() + */ if (!(tmp=(struct st_my_thread_var *) - calloc(1,sizeof(struct st_my_thread_var)))) + my_malloc(sizeof(struct st_my_thread_var),MYF(MY_WME|MY_ZEROFILL)))) { pthread_mutex_unlock(&THR_LOCK_lock); return 1; @@ -125,6 +139,9 @@ my_bool my_thread_init(void) pthread_setspecific(THR_KEY_mysys,tmp); #else + /* Sasha: TODO - explain what exactly we are doing on Windows + At first glance, I have a hard time following the code + */ if (THR_KEY_mysys.id) /* Already initialized */ { #if !defined(__WIN__) || defined(USE_TLS) || ! defined(SAFE_MUTEX) @@ -146,9 +163,18 @@ my_bool my_thread_init(void) void my_thread_end(void) { struct st_my_thread_var *tmp=my_thread_var; +#ifdef EXTRA_DEBUG + fprintf(stderr,"my_thread_end(): tmp=%p,thread_id=%ld\n", + tmp,pthread_self()); +#endif if (tmp) { #if !defined(DBUG_OFF) + /* Sasha: tmp->dbug is allocated inside DBUG library + so for now we will not mess with trying to use my_malloc()/ + my_free(), but in the future it would be nice to figure out a + way to do it + */ if (tmp->dbug) { free(tmp->dbug); @@ -160,12 +186,15 @@ void my_thread_end(void) #endif pthread_mutex_destroy(&tmp->mutex); #if (!defined(__WIN__) && !defined(OS2)) || defined(USE_TLS) - free(tmp); + /* we need to setspecific to 0 BEFORE we call my_free, as my_free + uses some DBUG_ macros that will use the follow the specific + pointer after the block it is pointing to has been freed if + specific does not get reset first + */ + pthread_setspecific(THR_KEY_mysys,0); + my_free((gptr)tmp,MYF(MY_WME)); #endif } -#if (!defined(__WIN__) && !defined(OS2)) || defined(USE_TLS) - pthread_setspecific(THR_KEY_mysys,0); -#endif } struct st_my_thread_var *_my_thread_var(void) diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c index d8c089c2ff0..062f9b9db02 100644 --- a/mysys/safemalloc.c +++ b/mysys/safemalloc.c @@ -73,14 +73,25 @@ #include "mysys_err.h" ulonglong safemalloc_mem_limit = ~(ulonglong)0; +pthread_t shutdown_th=0,main_th=0,signal_th=0; #define pNext tInt._pNext #define pPrev tInt._pPrev #define sFileName tInt._sFileName #define uLineNum tInt._uLineNum #define uDataSize tInt._uDataSize +#define thread_id tInt.thread_id #define lSpecialValue tInt._lSpecialValue +#ifndef PEDANTIC_SAFEMALLOC +static int sf_malloc_tampered = 0; /* set to 1 after TERMINATE() if we had + to fiddle with cNewCount and the linked + list of blocks so that _sanity() will + not fuss when it is not supposed to + */ +#endif + + /* Static functions prototypes */ static int check_ptr(const char *where, byte *ptr, const char *sFile, @@ -174,6 +185,7 @@ gptr _mymalloc (uint uSize, const char *sFile, uint uLine, myf MyFlags) pTmp -> sFileName = (my_string) sFile; pTmp -> uLineNum = uLine; pTmp -> uDataSize = uSize; + pTmp->thread_id = pthread_self(); pTmp -> pPrev = NULL; /* Add this remember structure to the linked list */ @@ -359,6 +371,12 @@ static int check_ptr(const char *where, byte *ptr, const char *sFile, return 0; } +static int legal_leak(struct remember* pPtr) +{ + return pthread_self() == pPtr->thread_id || main_th == pPtr->thread_id + || shutdown_th == pPtr->thread_id + || signal_th == pPtr->thread_id; +} /* * TERMINATE(FILE *file) @@ -376,6 +394,47 @@ void TERMINATE (FILE *file) /* NEW and the number of calls to FREE. >0 means more */ /* NEWs than FREEs. <0, etc. */ +#ifndef PEDANTIC_SAFEMALLOC + /* Avoid false alarms for blocks that we cannot free before my_end() + This does miss some positives, but that is ok. This will only miss + failures to free things allocated in the main thread which + performs only one-time allocations. If you really need to + debug memory allocations in the main thread, + #define PEDANTIC_SAFEMALLOC + */ + if ((pPtr=pRememberRoot)) + { + while (pPtr) + { + if (legal_leak(pPtr)) + { + sf_malloc_tampered=1; + cNewCount--; + lCurMemory -= pPtr->uDataSize; + if (pPtr->pPrev) + { + struct remember* tmp; + tmp = pPtr->pPrev->pNext = pPtr->pNext; + if (tmp) + tmp->pPrev = pPtr->pPrev; + pPtr->pNext = pPtr->pPrev = 0; + pPtr = tmp; + } + else + { + pRememberRoot = pPtr->pNext; + pPtr->pNext = pPtr->pPrev = 0; + pPtr = pRememberRoot; + if (pPtr) + pPtr->pPrev=0; + } + } + else + pPtr = pPtr->pNext; + } + } +#endif + if (cNewCount) { if (file) @@ -402,10 +461,14 @@ void TERMINATE (FILE *file) if (file) { fprintf (file, - "\t%6u bytes at 0x%09lx, allocated at line %4u in '%s'\n", + "\t%6u bytes at 0x%09lx, allocated at line %4u in '%s'", pPtr -> uDataSize, (ulong) &(pPtr -> aData[sf_malloc_prehunc]), pPtr -> uLineNum, pPtr -> sFileName); +#ifdef THREAD + fprintf(file, " in thread %ld", pPtr->thread_id); +#endif + fprintf(file, "\n"); (void) fflush(file); } DBUG_PRINT("safe", @@ -484,6 +547,10 @@ int _sanity (const char *sFile, uint uLine) uint count=0; pthread_mutex_lock(&THR_LOCK_malloc); +#ifndef PEDANTIC_SAFEMALLOC + if (sf_malloc_tampered && cNewCount < 0) + cNewCount=0; +#endif count=cNewCount; for (pTmp = pRememberRoot; pTmp != NULL && count-- ; pTmp = pTmp -> pNext) flag+=_checkchunk (pTmp, sFile, uLine); @@ -492,6 +559,7 @@ int _sanity (const char *sFile, uint uLine) { const char *format="Safemalloc link list destroyed, discovered at '%s:%d'"; fprintf (stderr, format, sFile, uLine); fputc('\n',stderr); + fprintf (stderr, "root=%p,count=%d,pTmp=%p\n", pRememberRoot,count,pTmp); (void) fflush(stderr); DBUG_PRINT("safe",(format, sFile, uLine)); flag=1; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index da93a481a5a..adaf524ccb2 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -38,7 +38,17 @@ #define ONE_THREAD #endif -/* do stack traces are only supported on linux intel */ +#ifdef SAFEMALLOC +#define SHUTDOWN_THD shutdown_th=pthread_self(); +#define MAIN_THD main_th=pthread_self(); +#define SIGNAL_THD signal_th=pthread_self(); +#else +#define SHUTDOWN_THD +#define MAIN_THD +#define SIGNAL_THD +#endif + +/* stack traces are only supported on linux intel */ #if defined(__linux__) && defined(__i386__) && defined(USE_PSTACK) #define HAVE_STACK_TRACE_ON_SEGV #include "../pstack/pstack.h" @@ -694,6 +704,7 @@ static void __cdecl kill_server(int sig_ptr) sql_print_error(ER(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */ #if defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2) + SHUTDOWN_THD; my_thread_init(); // If this is a new thread #endif close_connections(); @@ -709,6 +720,7 @@ static void __cdecl kill_server(int sig_ptr) #ifdef USE_ONE_SIGNAL_HAND static pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) { + SHUTDOWN_THD; my_thread_init(); // Initialize new thread kill_server(0); my_thread_end(); // Normally never reached @@ -1252,6 +1264,7 @@ static void init_signals(void) signal(SIGALRM, SIG_IGN); signal(SIGBREAK,SIG_IGN); signal_thread = pthread_self(); + SIGNAL_THD; } static void start_signal_handler(void) @@ -1445,7 +1458,7 @@ static void *signal_hand(void *arg __attribute__((unused))) int sig; my_thread_init(); // Init new thread DBUG_ENTER("signal_hand"); - + SIGNAL_THD; /* Setup alarm handler */ init_thr_alarm(max_connections+max_insert_delayed_threads); #if SIGINT != THR_KILL_SIGNAL @@ -1500,7 +1513,10 @@ static void *signal_hand(void *arg __attribute__((unused))) else while ((error=my_sigwait(&set,&sig)) == EINTR) ; if (cleanup_done) + { + my_thread_end(); pthread_exit(0); // Safety + } switch (sig) { case SIGTERM: case SIGQUIT: @@ -1594,6 +1610,7 @@ int uname(struct utsname *a) pthread_handler_decl(handle_shutdown,arg) { MSG msg; + SHUTDOWN_THD; my_thread_init(); /* this call should create the message queue for this thread */ @@ -1620,6 +1637,7 @@ int __stdcall handle_kill(ulong ctrl_type) #ifdef OS2 pthread_handler_decl(handle_shutdown,arg) { + SHUTDOWN_THD; my_thread_init(); // wait semaphore @@ -1691,6 +1709,7 @@ int main(int argc, char **argv) my_umask=0660; // Default umask for new files my_umask_dir=0700; // Default umask for new directories + MAIN_THD; MY_INIT(argv[0]); // init my_sys library & pthreads tzset(); // Set tzname -- cgit v1.2.1