diff options
-rw-r--r-- | client/mysql.cc | 6 | ||||
-rw-r--r-- | client/mysqladmin.cc | 17 | ||||
-rw-r--r-- | client/mysqlbinlog.cc | 26 | ||||
-rw-r--r-- | client/mysqlcheck.c | 2 | ||||
-rw-r--r-- | client/mysqlimport.c | 20 | ||||
-rw-r--r-- | client/mysqltest.cc | 4 | ||||
-rw-r--r-- | dbug/CMakeLists.txt | 11 | ||||
-rw-r--r-- | dbug/dbug.c | 362 | ||||
-rw-r--r-- | dbug/my_main.c | 7 | ||||
-rwxr-xr-x | dbug/tests-t.pl | 4 | ||||
-rw-r--r-- | dbug/tests.c | 9 | ||||
-rw-r--r-- | dbug/user.r | 22 | ||||
-rw-r--r-- | extra/perror.c | 2 | ||||
-rw-r--r-- | include/my_dbug.h | 9 | ||||
-rw-r--r-- | include/my_stacktrace.h | 1 | ||||
-rw-r--r-- | include/my_sys.h | 6 | ||||
-rw-r--r-- | include/my_valgrind.h | 1 | ||||
-rw-r--r-- | mysql-test/r/mysqldump.result | 2 | ||||
-rw-r--r-- | mysql-test/t/mysqldump.test | 2 | ||||
-rw-r--r-- | mysys/CMakeLists.txt | 16 | ||||
-rw-r--r-- | mysys/my_malloc.c | 6 | ||||
-rw-r--r-- | mysys/my_new.cc | 15 | ||||
-rw-r--r-- | mysys/mysys_priv.h | 9 | ||||
-rw-r--r-- | mysys/safemalloc.c | 341 | ||||
-rw-r--r-- | mysys/typelib.c | 3 | ||||
-rw-r--r-- | sql/ha_partition.cc | 1 | ||||
-rw-r--r-- | sql/mysqld.cc | 5 | ||||
-rw-r--r-- | storage/pbxt/src/ha_pbxt.cc | 1 |
28 files changed, 455 insertions, 455 deletions
diff --git a/client/mysql.cc b/client/mysql.cc index 5debadee26c..1074a7aaaed 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1786,11 +1786,13 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case 'V': usage(1); - exit(0); + status.exit_status= 0; + mysql_end(-1); case 'I': case '?': usage(0); - exit(0); + status.exit_status= 0; + mysql_end(-1); } return 0; } diff --git a/client/mysqladmin.cc b/client/mysqladmin.cc index 463ec37fae2..4a691ff4e14 100644 --- a/client/mysqladmin.cc +++ b/client/mysqladmin.cc @@ -301,20 +301,17 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), int main(int argc,char *argv[]) { - int error= 0, ho_error; + int error= 0; MYSQL mysql; char **commands, **save_argv; MY_INIT(argv[0]); mysql_init(&mysql); - if (load_defaults("my",load_default_groups,&argc,&argv)) - exit(1); + if ((error= load_defaults("my",load_default_groups,&argc,&argv))) + goto err1; save_argv = argv; /* Save for free_defaults */ - if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) - { - free_defaults(save_argv); - exit(ho_error); - } + if ((error=handle_options(&argc, &argv, my_long_options, get_one_option))) + goto err2; if (debug_info_flag) my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; if (debug_check_flag) @@ -463,6 +460,7 @@ int main(int argc,char *argv[]) } /* got connection */ mysql_close(&mysql); +err2: mysql_library_end(); my_free(opt_password); my_free(user); @@ -470,8 +468,9 @@ int main(int argc,char *argv[]) my_free(shared_memory_base_name); #endif free_defaults(save_argv); +err1: my_end(my_end_arg); - exit(error ? 1 : 0); + exit(error); return 0; } diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 4de571b546d..8869c3ac81b 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -45,7 +45,7 @@ #include "mysqld.h" -Rpl_filter *binlog_filter; +Rpl_filter *binlog_filter= 0; #define BIN_LOG_HEADER_SIZE 4 #define PROBE_HEADER_LEN (EVENT_LEN_OFFSET+4) @@ -77,7 +77,7 @@ static void error(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2); static bool one_database=0, to_last_remote_log= 0, disable_log_bin= 0; -static bool opt_hexdump= 0; +static bool opt_hexdump= 0, opt_version= 0; const char *base64_output_mode_names[]= {"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS}; TYPELIB base64_output_mode_typelib= @@ -1430,6 +1430,7 @@ static void cleanup() my_free(user); my_free(const_cast<char*>(dirname_for_local_load)); + delete binlog_filter; delete glob_description_event; if (mysql) mysql_close(mysql); @@ -1588,10 +1589,12 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), break; case 'V': print_version(); - exit(0); + opt_version= 1; + break; case '?': usage(); - exit(0); + opt_version= 1; + break; } if (tty_password) pass= get_tty_password(NullS); @@ -2303,24 +2306,26 @@ int main(int argc, char** argv) my_init_time(); // for time functions init_alloc_root(&s_mem_root, 16384, 0); + if (load_defaults("my", load_groups, &argc, &argv)) + exit(1); + if (!(binlog_filter= new Rpl_filter)) { error("Failed to create Rpl_filter"); exit(1); } - if (load_defaults("my", load_groups, &argc, &argv)) - exit(1); - defaults_argv= argv; parse_args(&argc, (char***)&argv); - if (!argc) + if (!argc || opt_version) { - usage(); + if (!argc) + usage(); + cleanup(); free_defaults(defaults_argv); my_end(my_end_arg); - exit(1); + exit(!opt_version); } if (opt_base64_output_mode == BASE64_OUTPUT_UNSPEC) @@ -2407,7 +2412,6 @@ int main(int argc, char** argv) my_fclose(result_file, MYF(0)); cleanup(); free_annotate_event(); - delete binlog_filter; free_root(&s_mem_root, MYF(0)); free_defaults(defaults_argv); my_free_open_file_info(); diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index 445f4522cef..68456d59041 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -961,6 +961,7 @@ static void safe_exit(int error) DBUG_VOID_RETURN; if (sock) mysql_close(sock); + sf_leaking_memory= 1; /* don't check for memory leaks */ exit(error); DBUG_VOID_RETURN; } @@ -972,7 +973,6 @@ int main(int argc, char **argv) char **defaults_argv; MY_INIT(argv[0]); - mysql_library_init(-1, 0, 0); /* ** Check out the args */ diff --git a/client/mysqlimport.c b/client/mysqlimport.c index 3b9939d7c6d..fabec74052d 100644 --- a/client/mysqlimport.c +++ b/client/mysqlimport.c @@ -62,6 +62,8 @@ static char *opt_plugin_dir= 0, *opt_default_auth= 0; static longlong opt_ignore_lines= -1; #include <sslopt-vars.h> +static char **argv_to_free; + #ifdef HAVE_SMEM static char *shared_memory_base_name=0; #endif @@ -475,10 +477,18 @@ static void db_disconnect(char *host, MYSQL *mysql) static void safe_exit(int error, MYSQL *mysql) { - if (ignore_errors) + if (error && ignore_errors) return; if (mysql) mysql_close(mysql); + +#ifdef HAVE_SMEM + my_free(shared_memory_base_name); +#endif + free_defaults(argv_to_free); + mysql_library_end(); + my_free(opt_password); + my_end(my_end_arg); exit(error); } @@ -597,7 +607,6 @@ error: int main(int argc, char **argv) { int error=0; - char **argv_to_free; MY_INIT(argv[0]); if (load_defaults("my",load_default_groups,&argc,&argv)) @@ -687,11 +696,6 @@ int main(int argc, char **argv) exitcode= error; db_disconnect(current_host, mysql); } - my_free(opt_password); -#ifdef HAVE_SMEM - my_free(shared_memory_base_name); -#endif - free_defaults(argv_to_free); - my_end(my_end_arg); + safe_exit(0, 0); return(exitcode); } diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 386577a4224..89e74a56bfc 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -1394,6 +1394,7 @@ static void cleanup_and_exit(int exit_code) } } + sf_leaking_memory= 0; /* all memory should be freed by now */ exit(exit_code); } @@ -8462,6 +8463,9 @@ int main(int argc, char **argv) MY_INIT(argv[0]); DBUG_ENTER("main"); + /* mysqltest has no way to free all its memory correctly */ + sf_leaking_memory= 1; + save_file[0]= 0; TMPDIR[0]= 0; diff --git a/dbug/CMakeLists.txt b/dbug/CMakeLists.txt index 0a9af25dba1..a4f30f75f97 100644 --- a/dbug/CMakeLists.txt +++ b/dbug/CMakeLists.txt @@ -17,17 +17,6 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/dbug ${CMAKE_SOURCE_DIR}/include ) -IF(WIN32) - SET(DEFAULT_SAFEMALLOC OFF) -ELSE() - SET(DEFAULT_SAFEMALLOC ON) -ENDIF() -OPTION(WITH_SAFEMALLOC ${DEFAULT_SAFEMALLOC} "Use safemalloc for debug builds. Will result in slower execution.") - -IF(WITH_SAFEMALLOC) - ADD_DEFINITIONS( -DSAFEMALLOC) -ENDIF() - SET(DBUG_SOURCES dbug.c) ADD_CONVENIENCE_LIBRARY(dbug ${DBUG_SOURCES}) TARGET_LINK_LIBRARIES(dbug mysys) diff --git a/dbug/dbug.c b/dbug/dbug.c index 299d3e0d5da..645aeb100fd 100644 --- a/dbug/dbug.c +++ b/dbug/dbug.c @@ -98,9 +98,6 @@ #include <process.h> #endif -#include <my_valgrind.h> /* TRASH */ -#include <my_stacktrace.h> /* my_safe_print_str */ - /* * Manifest constants which may be "tuned" if desired. */ @@ -131,6 +128,7 @@ #define SANITY_CHECK_ON (1 << 12) /* Check memory on every DBUG_ENTER/RETURN */ #define TRACE_ON ((uint)1 << 31) /* Trace enabled. MUST be the highest bit!*/ +#define sf_sanity() (0) #define TRACING (cs->stack->flags & TRACE_ON) #define DEBUGGING (cs->stack->flags & DEBUG_ON) @@ -207,8 +205,6 @@ static BOOLEAN init_done= FALSE; /* Set to TRUE when initialization done */ static struct settings init_settings; static const char *db_process= 0;/* Pointer to process name; argv[0] */ my_bool _dbug_on_= TRUE; /* FALSE if no debugging at all */ -static const char *unknown_func= "?func"; -static const char *unknown_file= "?file"; typedef struct _db_code_state_ { const char *process; /* Pointer to process name; usually argv[0] */ @@ -289,8 +285,6 @@ static void DbugExit(const char *why); static const char *DbugStrTok(const char *s); static void DbugVfprintf(FILE *stream, const char* format, va_list args); -static void DbugErr(CODE_STATE *, uint, const char* format, ...); - /* * Miscellaneous printf format strings. */ @@ -313,9 +307,6 @@ static void DbugErr(CODE_STATE *, uint, const char* format, ...); #define WRITABLE(pathname) (access(pathname, W_OK) == 0) #endif -static int sf_sanity(); -static void sf_terminate(); - /* ** Macros to allow dbugging with threads */ @@ -323,9 +314,6 @@ static void sf_terminate(); #include <my_pthread.h> static pthread_mutex_t THR_LOCK_dbug; -/* this mutex protects all sf_* variables, and nothing else*/ -static pthread_mutex_t sf_mutex; - static CODE_STATE *code_state(void) { CODE_STATE *cs, **cs_ptr; @@ -341,7 +329,6 @@ static CODE_STATE *code_state(void) { init_done=TRUE; pthread_mutex_init(&THR_LOCK_dbug, NULL); - pthread_mutex_init(&sf_mutex, NULL); bzero(&init_settings, sizeof(init_settings)); init_settings.out_file=stderr; init_settings.flags=OPEN_APPEND; @@ -354,8 +341,8 @@ static CODE_STATE *code_state(void) cs=(CODE_STATE*) DbugMalloc(sizeof(*cs)); bzero((uchar*) cs,sizeof(*cs)); cs->process= db_process ? db_process : "dbug"; - cs->func= unknown_func; - cs->file= unknown_file; + cs->func= "?func"; + cs->file= "?file"; cs->stack=&init_settings; *cs_ptr= cs; } @@ -1637,7 +1624,6 @@ void _db_end_() init_settings.keywords= 0; init_settings.processes= 0; FreeState(cs, &tmp, 0); - sf_terminate(); } @@ -2161,346 +2147,4 @@ const char* _db_get_func_(void) return cs->func; } -/* - prints the error message, followed by a stack trace - of the specified depth -*/ -static void DbugErr(CODE_STATE *cs, uint depth, const char* format, ...) -{ - va_list args; - va_start(args,format); - vfprintf(stderr, format, args); - va_end(args); - - if (cs || ((cs= code_state()))) - { - uint i= depth; - struct _db_stack_frame_ *frame= cs->framep; - while (i-- && frame) - { - fprintf(stderr, ", at %s", frame->func); - frame= frame->prev; - } - } - - fprintf(stderr, "\n"); -} - -/******************************************************************** - memory debugger - based on safemalloc, memory sub-system, written by Bjorn Benson -********************************************************************/ - -#ifndef SF_REMEMBER_FRAMES -#define SF_REMEMBER_FRAMES 16 -#endif - -/* - Structure that stores information of an allocated memory block - The data is at &struct_adr+sizeof(struct irem) - Note that sizeof(struct st_irem) % sizeof(double) == 0 -*/ -struct st_irem -{ - struct st_irem *next; /* Linked list of structures */ - struct st_irem *prev; /* Other link */ - size_t datasize; /* Size requested */ - const char *frame[SF_REMEMBER_FRAMES]; /* call stack */ - uint32 marker; /* Underrun marker value */ -}; - -/* - DBUG_MALLOC/DBUG_REALLOC/DBUG_FREE can be called even - before dbug is initialized. We cannot properly take into account - these calls, but we can at least wrap allocated memory - in st_irem's and check for overrun/underruns. - These special irem's - that are not linked into a global list - - are distinguished by a special value in the 'next' pointer. -*/ -#define NOT_LINKED ((struct st_irem *)1) - -size_t sf_malloc_mem_limit= (intptr)~0ULL; -static size_t sf_malloc_cur_memory= 0L; /* Current memory usage */ -static size_t sf_malloc_max_memory= 0L; /* Maximum memory usage */ - -static int sf_malloc_count= 0; /* Number of allocated chunks */ - -static void *sf_min_adress= (void*) (intptr)~0ULL, - *sf_max_adress= 0; - -static struct st_irem *sf_malloc_root = 0; - -#define MAGICSTART 0x14235296 /* A magic value for underrun key */ - -#define MAGICEND0 0x68 /* Magic values for overrun keys */ -#define MAGICEND1 0x34 /* " */ -#define MAGICEND2 0x7A /* " */ -#define MAGICEND3 0x15 /* " */ - -static int bad_ptr(const char *where, void *ptr); -static void free_memory(void *ptr); - -/* - * FUNCTION - * - * _db_malloc_ allocates memory - * - * SYNOPSIS - * - * void *_db_malloc_(size_t size) - * size_t size; Bytes to allocate - */ - -void *_db_malloc_(size_t size) -{ -#ifndef SAFEMALLOC - return malloc(size); -#else - CODE_STATE *cs= code_state(); - struct st_irem *irem; - uchar *data; - struct _db_stack_frame_ *frame; - int i= 0; - - if (size + sf_malloc_cur_memory > sf_malloc_mem_limit) - irem= 0; - else - irem= (struct st_irem *) malloc (sizeof(struct st_irem) + size + 4); - - if (!irem) - return 0; - - compile_time_assert(sizeof(struct st_irem) % sizeof(double) == 0); - - /* Fill up the structure */ - data= (uchar*) (irem + 1); - irem->datasize= size; - irem->prev= 0; - irem->marker= MAGICSTART; - data[size + 0]= MAGICEND0; - data[size + 1]= MAGICEND1; - data[size + 2]= MAGICEND2; - data[size + 3]= MAGICEND3; - - if (cs && cs->framep) - { - for (frame= cs->framep; - i < SF_REMEMBER_FRAMES && frame->func != unknown_func; - i++, frame= frame->prev) - irem->frame[i]= frame->func; - } - - if (i < SF_REMEMBER_FRAMES) - irem->frame[i]= unknown_func; - if (i==0) - irem->frame[0]= (char*)1; - - if (init_done) - { - pthread_mutex_lock(&sf_mutex); - /* Add this structure to the linked list */ - if ((irem->next= sf_malloc_root)) - sf_malloc_root->prev= irem; - sf_malloc_root= irem; - - /* Keep the statistics */ - sf_malloc_count++; - sf_malloc_cur_memory+= size; - set_if_bigger(sf_malloc_max_memory, sf_malloc_cur_memory); - set_if_smaller(sf_min_adress, (void*)data); - set_if_bigger(sf_max_adress, (void*)data); - pthread_mutex_unlock(&sf_mutex); - } - else - { - set_if_bigger(sf_malloc_max_memory, sf_malloc_cur_memory); - set_if_smaller(sf_min_adress, (void*)data); - set_if_bigger(sf_max_adress, (void*)data); - irem->next= NOT_LINKED; - } - - TRASH_ALLOC(data, size); - return data; -#endif -} - -void *_db_realloc_(void *ptr, size_t size) -{ -#ifndef SAFEMALLOC - return realloc(ptr, size); -#else - char *data; - - if (!ptr) - return _db_malloc_(size); - - if (bad_ptr("Reallocating", ptr)) - return 0; - - if ((data= _db_malloc_(size))) - { - struct st_irem *irem= (struct st_irem *)ptr - 1; - set_if_smaller(size, irem->datasize); - memcpy(data, ptr, size); - free_memory(ptr); - } - return data; -#endif -} - -void _db_free_(void *ptr) -{ -#ifndef SAFEMALLOC - free(ptr); -#else - if (!ptr || bad_ptr("Freeing", ptr)) - return; - - free_memory(ptr); -#endif -} - -static void free_memory(void *ptr) -{ - struct st_irem *irem= (struct st_irem *)ptr - 1; - - if (irem->next != NOT_LINKED) - { - pthread_mutex_lock(&sf_mutex); - /* Remove this structure from the linked list */ - if (irem->prev) - irem->prev->next= irem->next; - else - sf_malloc_root= irem->next; - - if (irem->next) - irem->next->prev= irem->prev; - - /* Handle the statistics */ - sf_malloc_cur_memory-= irem->datasize; - sf_malloc_count--; - pthread_mutex_unlock(&sf_mutex); - } - - /* only trash the data and magic values, but keep the stack trace */ - TRASH_FREE((uchar*)(irem + 1) - 4, irem->datasize + 8); - free(irem); - return; -} - -#define SF_ADD_NL 1 -#define SF_USE_SAFE_PRINT 2 -static void print_allocated_at(struct st_irem *irem, int flags) -{ - int i; - const char *allocated= flags & SF_ADD_NL ? "Allocated" : ", allocated"; - - for (i=0; - i < SF_REMEMBER_FRAMES && irem->frame[i] != unknown_func; - i++) - { - fprintf(stderr, "%s at ", i ? "," : allocated); - if (flags & SF_USE_SAFE_PRINT) - my_safe_print_str(irem->frame[i], 80); - else - fputs(irem->frame[i], stderr); - } - if (i && (flags & SF_ADD_NL)) - fprintf(stderr, "\n"); -} - -static int bad_ptr(const char *where, void *ptr) -{ - struct st_irem *irem= (struct st_irem *)ptr - 1; - const uchar *magicend; - - if (((intptr) ptr) % sizeof(double)) - { - DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s wrong aligned pointer", where); - return 1; - } - if (ptr < sf_min_adress || ptr > sf_max_adress) - { - DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s pointer out of range", where); - return 1; - } - if (irem->marker != MAGICSTART) - { - DbugErr(0, SF_REMEMBER_FRAMES, - "Error: %s unallocated data or underrun buffer", where); - /* - we cannot use print_allocated_at here: - if the memory was not allocated, there's nothing to print, - if it was allocated and underrun, call stack may be corrupted - */ - return 1; - } - - magicend= (uchar*)ptr + irem->datasize; - if (magicend[0] != MAGICEND0 || - magicend[1] != MAGICEND1 || - magicend[2] != MAGICEND2 || - magicend[3] != MAGICEND3) - { - DbugErr(0, SF_REMEMBER_FRAMES, "Error: %s overrun buffer", where); - print_allocated_at(irem, SF_ADD_NL); - return 1; - } - - return 0; -} - -/* check all allocated memory list for consistency */ -static int sf_sanity() -{ - struct st_irem *irem; - int flag= 0; - int count= 0; - - pthread_mutex_lock(&sf_mutex); - count= sf_malloc_count; - for (irem= sf_malloc_root; irem && count > 0; count--, irem= irem->next) - flag+= bad_ptr("Safemalloc", irem + 1); - pthread_mutex_unlock(&sf_mutex); - if (count || irem) - { - DbugErr(0, SF_REMEMBER_FRAMES, "Error: Safemalloc link list destroyed"); - return 1; - } - return 0; -} - -/* - * FUNCTION - * - * sf_terminate Report on all the memory pieces that have not been free'd - * - * SYNOPSIS - * - * void sf_terminate() - */ - -static void sf_terminate() -{ - struct st_irem *irem; - - sf_sanity(); - - /* Report on all the memory that was allocated but not free'd */ - if ((irem= sf_malloc_root)) - { - while (irem) - { - fprintf(stderr, "Warning: %6lu bytes at %p are not freed", (ulong) irem->datasize, irem + 1); - print_allocated_at(irem, SF_USE_SAFE_PRINT); - fprintf(stderr, "\n"); - irem= irem->next; - } - fprintf(stderr, "Memory lost: %lu bytes in %d chunks\n", - (ulong) sf_malloc_cur_memory, sf_malloc_count); - } - - return; -} - #endif /* DBUG_OFF */ diff --git a/dbug/my_main.c b/dbug/my_main.c index d8bffe662e2..2b3e92b53cc 100644 --- a/dbug/my_main.c +++ b/dbug/my_main.c @@ -4,6 +4,7 @@ */ #include <my_global.h> /* This includes dbug.h */ +#include <my_sys.h> #include <my_pthread.h> int main (argc, argv) @@ -12,7 +13,7 @@ char *argv[]; { register int result, ix; extern int factorial(int); - my_thread_global_init(); + MY_INIT(argv[0]); { DBUG_ENTER ("main"); @@ -29,6 +30,8 @@ char *argv[]; result = factorial (atoi(argv[ix])); printf ("%d\n", result); } - DBUG_RETURN (0); + DBUG_LEAVE; } + my_end(0); + exit(0); } diff --git a/dbug/tests-t.pl b/dbug/tests-t.pl index 9f517ba3efd..4803627eda2 100755 --- a/dbug/tests-t.pl +++ b/dbug/tests-t.pl @@ -201,7 +201,7 @@ func2: info: s=ko | | <func3 <main % ./tests t:d:-d,ret3:f:-f,func2 +d,dump,explain:P -dbug: >main +dbug-tests: >main dbug-tests: | >func1 dbug-tests: | | | >func3 dbug-tests: | | | <func3 @@ -216,7 +216,7 @@ dbug-tests: | | >func3 dbug-tests: | | <func3 dbug-tests: <main % ./tests t:d:-d,ret3:f:-f,func2 +d,dump,explain:P:F -dbug: tests.c: >main +dbug-tests: tests.c: >main dbug-tests: tests.c: | >func1 dbug-tests: tests.c: | | | >func3 dbug-tests: tests.c: | | | <func3 diff --git a/dbug/tests.c b/dbug/tests.c index 5fb84d48ffc..d96388e9816 100644 --- a/dbug/tests.c +++ b/dbug/tests.c @@ -5,6 +5,7 @@ char *push1=0; #include <my_global.h> /* This includes dbug.h */ +#include <my_sys.h> #include <my_pthread.h> #include <string.h> @@ -44,7 +45,7 @@ int main (int argc, char *argv[]) if (argc == 1) return 0; - my_thread_global_init(); + MY_INIT("dbug-tests"); dup2(1, 2); for (i = 1; i < argc; i++) @@ -56,7 +57,6 @@ int main (int argc, char *argv[]) } { DBUG_ENTER ("main"); - DBUG_PROCESS ("dbug-tests"); func1(); DBUG_EXECUTE_IF("dump", { @@ -78,6 +78,9 @@ int main (int argc, char *argv[]) DBUG_PRINT("explain", ("dbug explained: %s", s)); } func2(); - DBUG_RETURN (0); + DBUG_LEAVE; } + DBUG_SET(""); /* to not have my_end() in the traces */ + my_end(0); + return 0; } diff --git a/dbug/user.r b/dbug/user.r index 1ccd46bd21e..8d8a9ce6910 100644 --- a/dbug/user.r +++ b/dbug/user.r @@ -881,20 +881,6 @@ Modifying .I initial value does not affect threads that are already running. Obviously, these macros are only useful in the multi-threaded environment. -.SP 1 -.LI DBUG_MALLOC\ -.LI DBUG_REALLOC\ -.LI DBUG_FREE\ -When these macros are used instead of system malloc(), realloc(), and free(), -.I dbug -built-in memory debugger performs checks for memory overwrites, underwrites, -memory leaks, and accesses to uninitialized or freed memory. Memory leaks are -found as memory not deallocated at shutdown. Memory overwrites and underwrites -are detected when this memory is about to be freed (by -.B DBUG_FREE -macro), unless -.B S -flag is present in the debug control string (see below). .LE .SK @@ -1003,11 +989,9 @@ Most useful with macros used to temporarily alter the debugger state. .LI S -Check the memory allocated with -.B DBUG_MALLOC -and -.B DBUG_REALLOC -for overwrites/underwrites +When compiled with +.I safemalloc +this flag invokes "sanity" memory checks (for overwrites/underwrites) on each .B DBUG_ENTER and diff --git a/extra/perror.c b/extra/perror.c index af5d95d3c5b..8621bed3e11 100644 --- a/extra/perror.c +++ b/extra/perror.c @@ -419,6 +419,8 @@ int main(int argc,char *argv[]) if (unknown_error) free(unknown_error); + my_handler_error_unregister(); + my_end(0); exit(error); return error; } diff --git a/include/my_dbug.h b/include/my_dbug.h index 920ba4d4f90..fb5838e8b5c 100644 --- a/include/my_dbug.h +++ b/include/my_dbug.h @@ -60,9 +60,6 @@ extern void _db_unlock_file_(void); extern FILE *_db_fp_(void); extern void _db_flush_(); extern const char* _db_get_func_(void); -extern void *_db_malloc_(size_t size); -extern void *_db_realloc_(void *ptr, size_t size); -extern void _db_free_(void *ptr); #define DBUG_ENTER(a) struct _db_stack_frame_ _db_stack_frame_; \ _db_enter_ (a,__FILE__,__LINE__,&_db_stack_frame_) @@ -95,9 +92,6 @@ extern void _db_free_(void *ptr); #define DBUG_EXPLAIN_INITIAL(buf,len) _db_explain_init_((buf),(len)) #define DEBUGGER_OFF do { _dbug_on_= 0; } while(0) #define DEBUGGER_ON do { _dbug_on_= 1; } while(0) -#define DBUG_MALLOC(SIZE) _db_malloc_(SIZE) -#define DBUG_REALLOC(PTR,SIZE) _db_realloc_(PTR,SIZE) -#define DBUG_FREE(PTR) _db_free_(PTR) #define IF_DBUG(A,B) A #ifndef __WIN__ @@ -156,9 +150,6 @@ extern void _db_suicide_(); #define DBUG_EXPLAIN_INITIAL(buf,len) #define DEBUGGER_OFF do { } while(0) #define DEBUGGER_ON do { } while(0) -#define DBUG_MALLOC(SIZE) malloc(SIZE) -#define DBUG_REALLOC(PTR,SIZE) realloc(PTR,SIZE) -#define DBUG_FREE(PTR) free(PTR) #define IF_DBUG(A,B) B #define DBUG_ABORT() do { } while(0) #define DBUG_CRASH_ENTER(func) diff --git a/include/my_stacktrace.h b/include/my_stacktrace.h index f5229a4b0af..30f0203a43e 100644 --- a/include/my_stacktrace.h +++ b/include/my_stacktrace.h @@ -55,6 +55,7 @@ void my_set_exception_pointers(EXCEPTION_POINTERS *ep); #endif #endif +/* at the moment, safemalloc is the main user of libbfd */ #ifndef SAFEMALLOC #undef HAVE_BFD_H #endif diff --git a/include/my_sys.h b/include/my_sys.h index dbf999edac8..cb5fa317888 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -153,8 +153,10 @@ extern void *my_realloc(void *oldpoint, size_t Size, myf MyFlags); extern void my_free(void *ptr); extern void *my_memdup(const void *from,size_t length,myf MyFlags); extern char *my_strdup(const char *from,myf MyFlags); -extern char *my_strndup(const char *from, size_t length, - myf MyFlags); +extern char *my_strndup(const char *from, size_t length, myf MyFlags); + +extern int sf_leaking_memory; /* set to 1 to disable memleak detection */ + #if defined(ENABLED_DEBUG_SYNC) extern void (*debug_sync_C_callback_ptr)(const char *, size_t); #define DEBUG_SYNC_C(_sync_point_name_) do { \ diff --git a/include/my_valgrind.h b/include/my_valgrind.h index 87e6c3192dc..f82dd35b369 100644 --- a/include/my_valgrind.h +++ b/include/my_valgrind.h @@ -15,6 +15,7 @@ /* Some defines to make it easier to use valgrind */ +#include <m_string.h> /* bfill */ #ifdef HAVE_valgrind #define IF_VALGRIND(A,B) A diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index 1b0cca2a7b8..2ffc5716348 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -4292,7 +4292,7 @@ Abernathy aberrant aberration drop table words; -mysql-import: Error: 1146, Table 'test.words' doesn't exist, when using table: words +mysqlimport: Error: 1146, Table 'test.words' doesn't exist, when using table: words drop table t1; drop table t2; drop table words2; diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test index 19d47a9dac3..2170b9aa38b 100644 --- a/mysql-test/t/mysqldump.test +++ b/mysql-test/t/mysqldump.test @@ -1747,7 +1747,7 @@ select * from words2; # Drop table "words" and run with threads, should fail drop table words; ---replace_regex /.*mysqlimport(\.exe)*/mysql-import/ +--replace_regex /.*mysqlimport(\.exe)*/mysqlimport/ --error 1 --exec $MYSQL_IMPORT --silent --use-threads=2 test $MYSQLTEST_VARDIR/tmp/t1.txt $MYSQLTEST_VARDIR/tmp/t2.txt $MYSQLTEST_VARDIR/std_data/words.dat $MYSQLTEST_VARDIR/std_data/words2.dat 2>&1 diff --git a/mysys/CMakeLists.txt b/mysys/CMakeLists.txt index f258fd7bb7f..ec73140a42e 100644 --- a/mysys/CMakeLists.txt +++ b/mysys/CMakeLists.txt @@ -33,7 +33,7 @@ SET(MYSYS_SOURCES array.c charset-def.c charset.c checksum.c default.c rijndael.c sha1.c string.c thr_alarm.c thr_lock.c thr_mutex.c thr_rwlock.c tree.c typelib.c base64.c my_memmem.c my_getpagesize.c lf_alloc-pin.c lf_dynarray.c lf_hash.c - my_addr_resolve.c + my_addr_resolve.c safemalloc.c my_atomic.c my_getncpus.c my_safehash.c my_chmod.c my_rnd.c my_uuid.c wqueue.c waiting_threads.c ma_dyncol.c my_rdtsc.c) @@ -46,8 +46,18 @@ IF(HAVE_ALARM) SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_alarm.c) ENDIF() -IF(NOT HAVE_CXX_NEW) - # gcc as C++ compiler does not have new/delete +IF(WIN32) + SET(DEFAULT_SAFEMALLOC OFF) +ELSE() + SET(DEFAULT_SAFEMALLOC ON) +ENDIF() +OPTION(WITH_SAFEMALLOC "Use safemalloc for debug builds. Will result in slower execution." ${DEFAULT_SAFEMALLOC}) + +IF(WITH_SAFEMALLOC) + ADD_DEFINITIONS( -DSAFEMALLOC) +ENDIF() + +IF(NOT HAVE_CXX_NEW OR WITH_SAFEMALLOC) SET(MYSYS_SOURCES ${MYSYS_SOURCES} my_new.cc) ADD_DEFINITIONS( -DUSE_MYSYS_NEW) ENDIF() diff --git a/mysys/my_malloc.c b/mysys/my_malloc.c index 24bccf37ece..82fbe3a63f7 100644 --- a/mysys/my_malloc.c +++ b/mysys/my_malloc.c @@ -37,7 +37,7 @@ void *my_malloc(size_t size, myf my_flags) if (!size) size=1; - point= DBUG_MALLOC(size); + point= sf_malloc(size); DBUG_EXECUTE_IF("simulate_out_of_memory", { my_free(point); @@ -85,7 +85,7 @@ void *my_realloc(void *oldpoint, size_t size, myf my_flags) DBUG_ASSERT(size > 0); if (!oldpoint && (my_flags & MY_ALLOW_ZERO_PTR)) DBUG_RETURN(my_malloc(size, my_flags)); - if ((point= DBUG_REALLOC(oldpoint, size)) == NULL) + if ((point= sf_realloc(oldpoint, size)) == NULL) { if (my_flags & MY_FREE_ON_ERROR) my_free(oldpoint); @@ -111,7 +111,7 @@ void my_free(void *ptr) { DBUG_ENTER("my_free"); DBUG_PRINT("my",("ptr: %p", ptr)); - DBUG_FREE(ptr); + sf_free(ptr); DBUG_VOID_RETURN; } diff --git a/mysys/my_new.cc b/mysys/my_new.cc index 377d9be22a8..8724f9cc4a4 100644 --- a/mysys/my_new.cc +++ b/mysys/my_new.cc @@ -16,7 +16,10 @@ /* This is a replacement of new/delete operators to be used when compiling - with gcc 3.0.x to avoid including libstdc++ + with gcc 3.0.x to avoid including libstdc++ + + It is also used to make all memory allocations to go through + my_malloc/my_free wrappers (for debugging/safemalloc and accounting) */ #include "mysys_priv.h" @@ -25,24 +28,22 @@ void *operator new (size_t sz) { - return (void *) malloc (sz ? sz : 1); + return (void *) my_malloc (sz ? sz : 1, MYF(0)); } void *operator new[] (size_t sz) { - return (void *) malloc (sz ? sz : 1); + return (void *) my_malloc (sz ? sz : 1, MYF(0)); } void operator delete (void *ptr) { - if (ptr) - free(ptr); + my_free(ptr); } void operator delete[] (void *ptr) throw () { - if (ptr) - free(ptr); + my_free(ptr); } C_MODE_START diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index 2c0c8bf82f8..f5d2f301837 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -69,6 +69,15 @@ extern PSI_file_key key_file_proc_meminfo; extern PSI_file_key key_file_charset, key_file_cnf; #endif /* HAVE_PSI_INTERFACE */ +#ifdef SAFEMALLOC +void *sf_malloc(size_t size); +void *sf_realloc(void *ptr, size_t size); +void sf_free(void *ptr); +#else +#define sf_malloc(X) malloc(X) +#define sf_realloc(X,Y) realloc(X,Y) +#define sf_free(X) free(X) +#endif /* EDQUOT is used only in 3 C files only in mysys/. If it does not exist on diff --git a/mysys/safemalloc.c b/mysys/safemalloc.c new file mode 100644 index 00000000000..61ad9af0631 --- /dev/null +++ b/mysys/safemalloc.c @@ -0,0 +1,341 @@ +/* Copyright (C) 2000 MySQL AB, 2011 Monty Program Ab + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/******************************************************************** + memory debugger + based on safemalloc, memory sub-system, written by Bjorn Benson +********************************************************************/ + + +#include "mysys_priv.h" +#include <my_stacktrace.h> /* my_addr_resolve */ + +#if HAVE_EXECINFO_H +#include <execinfo.h> +#endif + +/* + this can be set to 1 if we leak memory and know it + (to disable memory leak tests on exit) +*/ +int sf_leaking_memory= 0; + +#ifdef SAFEMALLOC + +/* this mutex protects all sf_* variables, and nothing else*/ +static pthread_mutex_t sf_mutex; +static int init_done= 0; + +#ifndef SF_REMEMBER_FRAMES +#define SF_REMEMBER_FRAMES 8 +#endif + +/* ignore the first two frames (sf_malloc itself, and my_malloc) */ +#define SF_FRAMES_SKIP 2 + +/* + Structure that stores information of an allocated memory block + The data is at &struct_adr+sizeof(struct irem) + Note that sizeof(struct st_irem) % sizeof(double) == 0 +*/ +struct st_irem +{ + struct st_irem *next; /* Linked list of structures */ + struct st_irem *prev; /* Other link */ + size_t datasize; /* Size requested */ +#ifdef HAVE_BACKTRACE + void *frame[SF_REMEMBER_FRAMES]; /* call stack */ +#endif + uint32 marker; /* Underrun marker value */ +}; + +static int sf_malloc_count= 0; /* Number of allocated chunks */ + +static void *sf_min_adress= (void*) (intptr)~0ULL, + *sf_max_adress= 0; + +static struct st_irem *sf_malloc_root = 0; + +#define MAGICSTART 0x14235296 /* A magic value for underrun key */ + +#define MAGICEND0 0x68 /* Magic values for overrun keys */ +#define MAGICEND1 0x34 /* " */ +#define MAGICEND2 0x7A /* " */ +#define MAGICEND3 0x15 /* " */ + +static int bad_ptr(const char *where, void *ptr); +static void free_memory(void *ptr); +static void sf_terminate(); + +/** + allocates memory +*/ + +void *sf_malloc(size_t size) +{ + struct st_irem *irem; + uchar *data; + + /* + this style of initialization looks like race conditon prone, + but it is safe under the assumption that a program does + at least one malloc() while still being single threaded. + */ + if (!init_done) + { + pthread_mutex_init(&sf_mutex, NULL); + /* disable deadlock detector, because it calls my_malloc() */ + safe_mutex_setflags(&sf_mutex, MYF_NO_DEADLOCK_DETECTION); + atexit(sf_terminate); + init_done= 1; + } + + irem= (struct st_irem *) malloc (sizeof(struct st_irem) + size + 4); + + if (!irem) + return 0; + + /* we guarantee the alignment */ + compile_time_assert(sizeof(struct st_irem) % sizeof(double) == 0); + + /* Fill up the structure */ + data= (uchar*) (irem + 1); + irem->datasize= size; + irem->prev= 0; + irem->marker= MAGICSTART; + data[size + 0]= MAGICEND0; + data[size + 1]= MAGICEND1; + data[size + 2]= MAGICEND2; + data[size + 3]= MAGICEND3; + +#ifdef HAVE_BACKTRACE + { + void *frame[SF_REMEMBER_FRAMES + SF_FRAMES_SKIP]; + int frames= backtrace(frame, array_elements(frame)); + if (frames < SF_FRAMES_SKIP) + frames= 0; + else + { + frames-= SF_FRAMES_SKIP; + memcpy(irem->frame, frame + SF_FRAMES_SKIP, sizeof(void*)*frames); + } + if (frames < SF_REMEMBER_FRAMES) + irem->frame[frames]= 0; + } +#endif + + pthread_mutex_lock(&sf_mutex); + + /* Add this structure to the linked list */ + if ((irem->next= sf_malloc_root)) + sf_malloc_root->prev= irem; + sf_malloc_root= irem; + + /* Keep the statistics */ + sf_malloc_count++; + set_if_smaller(sf_min_adress, (void*)data); + set_if_bigger(sf_max_adress, (void*)data); + + pthread_mutex_unlock(&sf_mutex); + + TRASH_ALLOC(data, size); + return data; +} + +void *sf_realloc(void *ptr, size_t size) +{ + char *data; + + if (!ptr) + return sf_malloc(size); + + if (bad_ptr("Reallocating", ptr)) + return 0; + + if ((data= sf_malloc(size))) + { + struct st_irem *irem= (struct st_irem *)ptr - 1; + set_if_smaller(size, irem->datasize); + memcpy(data, ptr, size); + free_memory(ptr); + } + return data; +} + +void sf_free(void *ptr) +{ + if (!ptr || bad_ptr("Freeing", ptr)) + return; + + free_memory(ptr); +} + +static void free_memory(void *ptr) +{ + struct st_irem *irem= (struct st_irem *)ptr - 1; + + pthread_mutex_lock(&sf_mutex); + /* Remove this structure from the linked list */ + if (irem->prev) + irem->prev->next= irem->next; + else + sf_malloc_root= irem->next; + + if (irem->next) + irem->next->prev= irem->prev; + + /* Handle the statistics */ + sf_malloc_count--; + pthread_mutex_unlock(&sf_mutex); + + /* only trash the data and magic values, but keep the stack trace */ + TRASH_FREE((uchar*)(irem + 1) - 4, irem->datasize + 8); + free(irem); + return; +} + +#ifdef HAVE_BACKTRACE +static void print_stack(void **frame) +{ + const char *err; + int i; + + if ((err= my_addr_resolve_init())) + { + fprintf(stderr, "(my_addr_resolve failure: %s)\n", err); + return; + } + + for (i=0; i < SF_REMEMBER_FRAMES && frame[i]; i++) + { + my_addr_loc loc; + if (i) + fprintf(stderr, ", "); + + if (my_addr_resolve(frame[i], &loc)) + fprintf(stderr, "..."); + else + fprintf(stderr, "%s:%u", loc.file, loc.line); + } + fprintf(stderr, "\n"); +} +#else +#define print_stack(X) fprintf(stderr, "???\n") +#endif + +static void warn(const char *format,...) +{ + va_list args; + va_start(args,format); + vfprintf(stderr, format, args); + va_end(args); + +#ifdef HAVE_BACKTRACE + { + void *frame[SF_REMEMBER_FRAMES + SF_FRAMES_SKIP]; + int frames= backtrace(frame, array_elements(frame)); + if (frames < SF_REMEMBER_FRAMES + SF_FRAMES_SKIP) + frame[frames]= 0; + print_stack(frame + SF_FRAMES_SKIP); + } +#endif +} + +static int bad_ptr(const char *where, void *ptr) +{ + struct st_irem *irem= (struct st_irem *)ptr - 1; + const uchar *magicend; + + if (((intptr) ptr) % sizeof(double)) + { + warn("Error: %s wrong aligned pointer", where); + return 1; + } + if (ptr < sf_min_adress || ptr > sf_max_adress) + { + warn("Error: %s pointer out of range", where); + return 1; + } + if (irem->marker != MAGICSTART) + { + warn("Error: %s unallocated data or underrun buffer", where); + return 1; + } + + magicend= (uchar*)ptr + irem->datasize; + if (magicend[0] != MAGICEND0 || + magicend[1] != MAGICEND1 || + magicend[2] != MAGICEND2 || + magicend[3] != MAGICEND3) + { + warn("Error: %s overrun buffer", where); + fprintf(stderr, ", allocated at "); + print_stack(irem->frame); + return 1; + } + + return 0; +} + +/* check all allocated memory list for consistency */ +static int sf_sanity() +{ + struct st_irem *irem; + int flag= 0; + int count= 0; + + pthread_mutex_lock(&sf_mutex); + count= sf_malloc_count; + for (irem= sf_malloc_root; irem && count > 0; count--, irem= irem->next) + flag+= bad_ptr("Safemalloc", irem + 1); + pthread_mutex_unlock(&sf_mutex); + if (count || irem) + { + warn("Error: Safemalloc link list destroyed"); + return 1; + } + return 0; +} + +/** + report on all the memory pieces that have not been free'd +*/ + +static void sf_terminate() +{ + size_t total= 0; + struct st_irem *irem; + + sf_sanity(); + + /* Report on all the memory that was allocated but not free'd */ + if (!sf_leaking_memory && sf_malloc_root) + { + for (irem= sf_malloc_root; irem; irem= irem->next) + { + fprintf(stderr, "Warning: %4lu bytes lost, allocated at ", + (ulong) irem->datasize); + print_stack(irem->frame); + total+= irem->datasize; + } + fprintf(stderr, "Memory lost: %lu bytes in %d chunks\n", + (ulong) total, sf_malloc_count); + } + + pthread_mutex_destroy(&sf_mutex); + return; +} + +#endif diff --git a/mysys/typelib.c b/mysys/typelib.c index f724e5b27a2..3b4e1014f15 100644 --- a/mysys/typelib.c +++ b/mysys/typelib.c @@ -47,7 +47,10 @@ int find_type_or_exit(const char *x, TYPELIB *typelib, const char *option) { int res; if ((res= find_type_with_warning(x, typelib, option)) <= 0) + { + sf_leaking_memory= 1; /* no memory leak reports here */ exit(1); + } return res; } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b4dc3f79e0c..a1e45b36b82 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -6782,6 +6782,7 @@ int ha_partition::final_add_index(handler_add_index *add, bool commit) if (table_arg->key_info == add->key_info) table_arg->key_info= NULL; } + delete add; DBUG_RETURN(0); } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index baba6d45acf..564a8769b6b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -333,8 +333,6 @@ static PSI_rwlock_key key_rwlock_openssl; #endif #endif /* HAVE_PSI_INTERFACE */ -#undef SAFEMALLOC - /* the default log output is log tables */ static bool lower_case_table_names_used= 0; static bool max_long_data_size_used= false; @@ -3509,6 +3507,8 @@ static int init_common_variables() tzset(); // Set tzname + sf_leaking_memory= 0; // no memory leaks from now on + max_system_variables.pseudo_thread_id= (ulong)~0; server_start_time= flush_status_time= my_time(0); @@ -4709,6 +4709,7 @@ int mysqld_main(int argc, char **argv) to be able to read defaults files and parse options. */ my_progname= argv[0]; + sf_leaking_memory= 1; // no safemalloc memory leak reports if we exit early #ifndef _WIN32 // For windows, my_init() is called from the win specific mysqld_main if (my_init()) // init my_sys library & pthreads diff --git a/storage/pbxt/src/ha_pbxt.cc b/storage/pbxt/src/ha_pbxt.cc index f574dbd362a..ff8c85d1977 100644 --- a/storage/pbxt/src/ha_pbxt.cc +++ b/storage/pbxt/src/ha_pbxt.cc @@ -5732,6 +5732,7 @@ int ha_pbxt::create(const char *table_path, TABLE *table_arg, HA_CREATE_INFO *cr catch_(a) { if (tab_def) tab_def->finalize(self); + delete tab_def; dic.dic_table = NULL; err = xt_ha_pbxt_thread_error_for_mysql(thd, self, FALSE); } |