=== added file 'mysql-test/suite/sys_vars/r/innodb_dump_full_corefile_basic.result' --- mysql-test/suite/sys_vars/r/innodb_dump_full_corefile_basic.result 1970-01-01 00:00:00 +0000 +++ mysql-test/suite/sys_vars/r/innodb_dump_full_corefile_basic.result 2014-02-27 06:27:07 +0000 @@ -0,0 +1,20 @@ +Valid values are 'ON' and 'OFF' +select @@global.innodb_dump_full_corefile; +@@global.innodb_dump_full_corefile +1 +select @@session.innodb_dump_full_corefile; +ERROR HY000: Variable 'innodb_dump_full_corefile' is a GLOBAL variable +show global variables like 'innodb_dump_full_corefile'; +Variable_name Value +innodb_dump_full_corefile ON +show session variables like 'innodb_dump_full_corefile'; +Variable_name Value +innodb_dump_full_corefile ON +select * from information_schema.global_variables where variable_name='innodb_dump_full_corefile'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_DUMP_FULL_COREFILE ON +select * from information_schema.session_variables where variable_name='innodb_dump_full_corefile'; +VARIABLE_NAME VARIABLE_VALUE +INNODB_DUMP_FULL_COREFILE ON +set global innodb_dump_full_corefile=OFF; +set global innodb_dump_full_corefile=ON; === added file 'mysql-test/suite/sys_vars/t/innodb_dump_full_corefile_basic.test' --- mysql-test/suite/sys_vars/t/innodb_dump_full_corefile_basic.test 1970-01-01 00:00:00 +0000 +++ mysql-test/suite/sys_vars/t/innodb_dump_full_corefile_basic.test 2014-02-27 06:26:52 +0000 @@ -0,0 +1,17 @@ +--source include/have_innodb.inc + +# Can only be set from the command line. +# show the global and session values; + +--echo Valid values are 'ON' and 'OFF' +select @@global.innodb_dump_full_corefile; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.innodb_dump_full_corefile; +show global variables like 'innodb_dump_full_corefile'; +show session variables like 'innodb_dump_full_corefile'; +select * from information_schema.global_variables where variable_name='innodb_dump_full_corefile'; +select * from information_schema.session_variables where variable_name='innodb_dump_full_corefile'; + +set global innodb_dump_full_corefile=OFF; +set global innodb_dump_full_corefile=ON; + === modified file 'storage/innobase/handler/ha_innodb.cc' --- storage/innobase/handler/ha_innodb.cc 2014-02-24 06:03:53 +0000 +++ storage/innobase/handler/ha_innodb.cc 2014-02-27 10:28:01 +0000 @@ -14876,6 +14876,24 @@ return; } +/****************************************************************//** +Update the system variable innodb_dump_full_corefile on Linux that +since 3.4. */ +static +void +dump_full_corefile_update( +/*==============================*/ + THD* thd, /*!< in: thread handle */ + struct st_mysql_sys_var* var, /*!< in: pointer to + system variable */ + void* var_ptr,/*!< out: where the + formal string goes */ + const void* save) /*!< in: immediate result + from check function */ +{ + ut_advise_core_dump((*(my_bool*) save)); +} + #ifdef __WIN__ /*************************************************************//** Validate if passed-in "value" is a valid value for @@ -15892,6 +15910,11 @@ "Dump the buffer pool into a file named @@innodb_buffer_pool_filename", NULL, NULL, FALSE); +static MYSQL_SYSVAR_BOOL(dump_full_corefile, srv_dump_full_corefile, + PLUGIN_VAR_OPCMDARG, + "Dump the buffer pool into a corefile or not. Default is FALSE", + NULL, dump_full_corefile_update, FALSE); + #ifdef UNIV_DEBUG static MYSQL_SYSVAR_STR(buffer_pool_evict, srv_buffer_pool_evict, PLUGIN_VAR_RQCMDARG, @@ -16343,6 +16366,7 @@ MYSQL_SYSVAR(buffer_pool_filename), MYSQL_SYSVAR(buffer_pool_dump_now), MYSQL_SYSVAR(buffer_pool_dump_at_shutdown), + MYSQL_SYSVAR(dump_full_corefile), #ifdef UNIV_DEBUG MYSQL_SYSVAR(buffer_pool_evict), #endif /* UNIV_DEBUG */ === modified file 'storage/innobase/include/srv0srv.h' --- storage/innobase/include/srv0srv.h 2014-02-24 06:03:53 +0000 +++ storage/innobase/include/srv0srv.h 2014-02-27 06:00:57 +0000 @@ -150,6 +150,10 @@ extern char srv_buffer_pool_dump_at_shutdown; extern char srv_buffer_pool_load_at_startup; +/* Boolean config knobs that tell Linux kernel whether do full dump (such as + * with buffer pool) into corefile if core-file enabled. */ +extern char srv_dump_full_corefile; + /* Whether to disable file system cache if it is defined */ extern char srv_disable_sort_file_cache; === modified file 'storage/innobase/include/ut0mem.h' --- storage/innobase/include/ut0mem.h 2013-06-10 20:44:22 +0000 +++ storage/innobase/include/ut0mem.h 2014-02-27 06:10:05 +0000 @@ -254,6 +254,34 @@ ulint buf_size); /*!< in: output buffer size in bytes */ +/*******************************************************************//** +Utililty to trace allocated memory for core dumping. */ +UNIV_INTERN +void +ut_trace_mem( +/*==============*/ + void *ptr, /*!< in: allocated address pointer */ + size_t size); /*!< in: size of ptr in bytes */ + +/*******************************************************************//** +Utililty to clean allocated memory for core dumping. */ +UNIV_INTERN +void +ut_remove_mem( +/*==============*/ + void *ptr, /*!< in: allocated address pointer */ + size_t size); /*!< in: size of ptr in bytes */ + + +/*******************************************************************//** +Utililty to control whether do full corefile dump by Linux with +traced allocated addresses. */ +UNIV_INTERN +void +ut_advise_core_dump( +/*==============*/ + ibool do_full_dump); /*!< in: flag to do full corefile dump or not */ + #ifndef UNIV_NONINL #include "ut0mem.ic" #endif === modified file 'storage/innobase/os/os0proc.cc' --- storage/innobase/os/os0proc.cc 2013-06-10 20:44:22 +0000 +++ storage/innobase/os/os0proc.cc 2014-02-27 10:20:29 +0000 @@ -32,6 +32,10 @@ #include "ut0mem.h" #include "ut0byte.h" +#ifndef MADV_DONTDUMP +#define MADV_DONTDUMP 16 +#endif + /* FreeBSD for example has only MAP_ANON, Linux has MAP_ANONYMOUS and MAP_ANON but MAP_ANON is marked as deprecated */ #if defined(MAP_ANONYMOUS) @@ -165,6 +169,7 @@ os_fast_mutex_lock(&ut_list_mutex); ut_total_allocated_memory += size; os_fast_mutex_unlock(&ut_list_mutex); + ut_trace_mem(ptr, size); UNIV_MEM_ALLOC(ptr, size); } #endif @@ -226,6 +231,7 @@ ut_a(ut_total_allocated_memory >= size); ut_total_allocated_memory -= size; os_fast_mutex_unlock(&ut_list_mutex); + ut_remove_mem(ptr, size); UNIV_MEM_FREE(ptr, size); } #endif === modified file 'storage/innobase/srv/srv0srv.cc' --- storage/innobase/srv/srv0srv.cc 2013-11-29 12:56:54 +0000 +++ storage/innobase/srv/srv0srv.cc 2014-02-27 10:26:36 +0000 @@ -598,6 +598,10 @@ UNIV_INTERN char srv_buffer_pool_dump_at_shutdown = FALSE; UNIV_INTERN char srv_buffer_pool_load_at_startup = FALSE; +/* Boolean config knobs that tell Linux kernel whether do full dump (such as + * with buffer pool) into corefile if core-file enabled. */ +UNIV_INTERN char srv_dump_full_corefile = FALSE; + /** Slot index in the srv_sys->sys_threads array for the purge thread. */ static const ulint SRV_PURGE_SLOT = 1; === modified file 'storage/innobase/ut/ut0mem.cc' --- storage/innobase/ut/ut0mem.cc 2013-06-10 20:44:22 +0000 +++ storage/innobase/ut/ut0mem.cc 2014-02-27 10:25:16 +0000 @@ -35,6 +35,15 @@ #include +/* Backported from Linux kernel that introduced since Linux 3.4 to make + * compiler happy */ +#ifndef MADV_DONTDUMP +#define MADV_DONTDUMP 16 +#endif +#ifndef MADV_DODUMP +#define MADV_DODUMP 17 +#endif + /** The total amount of memory currently allocated from the operating system with os_mem_alloc_large() or malloc(). Does not count malloc() if srv_use_sys_malloc is set. Protected by ut_list_mutex. */ @@ -43,9 +52,13 @@ /** Mutex protecting ut_total_allocated_memory and ut_mem_block_list */ UNIV_INTERN os_fast_mutex_t ut_list_mutex; +/** Mutex protecting ut_mem_list */ +UNIV_INTERN os_fast_mutex_t ut_mem_mutex; + #ifdef UNIV_PFS_MUTEX /* Key to register server_mutex with performance schema */ UNIV_INTERN mysql_pfs_key_t ut_list_mutex_key; +UNIV_INTERN mysql_pfs_key_t ut_mem_mutex_key; #endif /** Dynamically allocated memory block */ @@ -56,6 +69,13 @@ ulint magic_n;/*!< magic number (UT_MEM_MAGIC_N) */ }; +/** Trace allocated memory block address */ +struct ut_mem_t { + UT_LIST_NODE_T(ut_mem_t) mem_list; /*!< mem block address list node */ + void* start; /*!< start address for allocated ptr */ + size_t len; /*!< lenght for allocated ptr */ +}; + /** The value of ut_mem_block_t::magic_n. Used in detecting memory corruption. */ #define UT_MEM_MAGIC_N 1601650166 @@ -64,9 +84,16 @@ with malloc. Protected by ut_list_mutex. */ static UT_LIST_BASE_NODE_T(ut_mem_block_t) ut_mem_block_list; +/** List of traced memory blocks allocated from the operating system +with malloc. Protected by ut_mem_mutex. */ +static UT_LIST_BASE_NODE_T(ut_mem_t) ut_mem_list; + /** Flag: has ut_mem_block_list been initialized? */ static ibool ut_mem_block_list_inited = FALSE; +/** Flag: has ut_mem_list been initialized? */ +static ibool ut_mem_list_inited = FALSE; + /** A dummy pointer for generating a null pointer exception in ut_malloc_low() */ static ulint* ut_mem_null_ptr = NULL; @@ -205,6 +232,87 @@ #endif /* !UNIV_HOTBACKUP */ } +/*******************************************************************//** +Utililty to trace allocated memory for core dumping. */ +UNIV_INTERN +void +ut_trace_mem( +/*==============*/ + void *ptr, /*!< in: allocated address pointer */ + size_t size) /*!< in: size of ptr in bytes */ +{ + if (!ut_mem_list_inited) { + os_fast_mutex_init(ut_mem_mutex_key, &ut_mem_mutex); + UT_LIST_INIT(ut_mem_list); + ut_mem_list_inited = TRUE; + } + + os_fast_mutex_lock(&ut_mem_mutex); + void *m = malloc(sizeof(ut_mem_t)); + ((ut_mem_t*) m)->start = ptr; + ((ut_mem_t*) m)->len = size; + UT_LIST_ADD_LAST(mem_list, ut_mem_list, ((ut_mem_t*)m)); + os_fast_mutex_unlock(&ut_mem_mutex); + + if (!srv_dump_full_corefile) + madvise(ptr, size, MADV_DONTDUMP); +} + +/*******************************************************************//** +Utililty to clean allocated memory for core dumping. */ +UNIV_INTERN +void +ut_remove_mem( +/*==============*/ + void *ptr, /*!< in: allocated address pointer */ + size_t size) /*!< in: size of ptr in bytes */ +{ + os_fast_mutex_lock(&ut_mem_mutex); + ut_mem_t *m; + + for (m = UT_LIST_GET_FIRST(ut_mem_list); + m; + m = UT_LIST_GET_NEXT(mem_list, m)) { + + if (m->start == ptr && m->len == size) { + UT_LIST_REMOVE(mem_list, ut_mem_list, m); + free(m); + break; + } + } + + os_fast_mutex_unlock(&ut_mem_mutex); + + if (UT_LIST_GET_LEN(ut_mem_list) == 0) { + os_fast_mutex_free(&ut_mem_mutex); + ut_mem_list_inited = FALSE; + } +} + +/*******************************************************************//** +Utililty to control whether do full corefile dump by Linux with +traced allocated addresses. */ +UNIV_INTERN +void +ut_advise_core_dump( +/*==============*/ + ibool do_full_dump) /*!< in: flag to do full corefile dump or not */ +{ + os_fast_mutex_lock(&ut_mem_mutex); + ut_mem_t *m; + for (m = UT_LIST_GET_FIRST(ut_mem_list); + m; + m = UT_LIST_GET_NEXT(mem_list, m)) { + + if (do_full_dump) + madvise(m->start, m->len, MADV_DODUMP); + else + madvise(m->start, m->len, MADV_DONTDUMP); + } + os_fast_mutex_unlock(&ut_mem_mutex); +} + + /**********************************************************************//** Frees a memory block allocated with ut_malloc. Freeing a NULL pointer is a nop. */