diff --git a/mysql-test/include/linux-version.inc b/mysql-test/include/linux-version.inc new file mode 100644 index 0000000000000..990c94012074b --- /dev/null +++ b/mysql-test/include/linux-version.inc @@ -0,0 +1,53 @@ +# Verifies that we are on linux, and that version returned by uname -r is +# at least as large as $minimum_required_linux_version. +# Otherwise it skips the test. + +--source 'include/linux.inc' + +if (!$minimum_required_linux_version){ + --die You must specify $minimum_required_linux_version +} + +let MINIMUM_REQUIRED_LINUX_VERSION_FOR_PERL = $minimum_required_linux_version; +let LINUX_VERSION_RESULT_FILE = $MYSQLTEST_VARDIR/log/linux_version_result.inc; + +perl; + use strict; + + # To keep it simple we only look at major and minor parts of uname -r, + # as later parts may contain non-digits and it is not clear how we + # should handle that. + # Don't be tempted to use $Config{osvers} here, as there are machines + # on which `uname` correctly reports 2.6 and $Config reports 3.8. + my $version= (split /\n/, `uname -r | cut -d '.' -f 1-2`)[0]; + my $minimum_required_version= $ENV{'MINIMUM_REQUIRED_LINUX_VERSION_FOR_PERL'}; + + # Retrieving result from --perl command is non-trivial as of today, so + # we need to create an *.inc file on the fly, that will contain result. + open(RESULT_FILE, ">$ENV{'LINUX_VERSION_RESULT_FILE'}"); + + # This would be much easier using CPAN::Version, but we can't rely on it + # being avialable in the environment. This simple implementation cares only + # about major and minor numbers. It was tested for 4 < 4.1, 4.1 < 4.10, + # 4.12 < 4.111 and 3.4 < 4.1 (and their mirror images). + + my @version_parts = split /\./, $version; + my @minimum_required_version_parts = split /\./, $minimum_required_version; + + if ((@version_parts[0] <=> @minimum_required_version_parts[0] || + @version_parts[1] <=> @minimum_required_version_parts[1]) < 0) { + print RESULT_FILE "let \$linux_version_is_ok = 0;\n"; + print RESULT_FILE "let \$found_linux_version = $version;\n"; + } else { + print RESULT_FILE "let \$linux_version_is_ok = 1;\n"; + } + close(RESULT_FILE); +EOF + +--source $LINUX_VERSION_RESULT_FILE +--remove_file $LINUX_VERSION_RESULT_FILE + +if (!$linux_version_is_ok) +{ + skip Needs Linux $minimum_required_linux_version, found $found_linux_version; +} diff --git a/mysql-test/include/mysqld_core_dump.inc b/mysql-test/include/mysqld_core_dump.inc new file mode 100644 index 0000000000000..10a031fd9d4ce --- /dev/null +++ b/mysql-test/include/mysqld_core_dump.inc @@ -0,0 +1,97 @@ +--echo # Get the full path name of the PID file +--let $pid_file= query_get_value(SELECT @@pid_file, @@pid_file, 1) +--let PIDFILE= $pid_file + +--echo # Expecting a "crash", but don't restart the server until it is told to +--echo # Expected max core size is $expected_max_core_size MB +--let MAXCORESIZE= $expected_max_core_size + +--exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +perl; + +my $pid_file = $ENV{'PIDFILE'} or die "PIDFILE not set"; +my $expected_max_core_size = $ENV{'MAXCORESIZE'} or die "MAXCORESIZE not set"; + +# The argument is in MB +$expected_max_core_size = $expected_max_core_size * 1024 * 1024; + +# Get PID of mysqld +open(my $fh, '<', $pid_file) || die "Cannot open pid file $pid_file\n"; +my $pid = <$fh>; +$pid =~ s/\s//g; +close($fh); + +if ($pid eq "") { + die "Couldn't retrieve PID from PID file.\n"; +} + +# The current time in seconds since epoch +$cur_time = time; + +# Kill mysqld to dump a core +system("kill", "-s", "SIGABRT", "$pid"); +print "# Perl: Sent a SIGABRT to mysqld to dump a core.\n"; + +$core_dir = $ENV{'MYSQLTEST_VARDIR'} . '/mysqld.1/data/'; + +$found_core = 0; +$core_size = 0; +$core_size_good = 0; + +# Check the files in the core file directory +$wait_sec = 60; +while ($wait_sec > 0) { + opendir(my $dir, $core_dir) or die "Failed to open dir $core_dir: $!\n"; + while (my $file = readdir($dir)) { + # If the core file name contains the PID + if (index($file, $pid) != -1) { + # The last write time in seconds since epoch + $full_path = $core_dir . '/' . $file; + @stat = stat($full_path); + $core_size = $stat[7]; + $write_secs = $stat[9]; + + # If the file was written within a minute + if ($cur_time <= $write_secs && $write_secs - $cur_time < 60) { + $found_core = 1; + if ($core_size < $expected_max_core_size) { + $core_size_good = 1; + } + # Remove the core file to avoid it get accumulated over time + unlink $full_path; + last; + } + } + } + closedir($dir); + + if ($found_core) { + last; + } + # Sleep 1 second and try again + --$wait_sec; + sleep 1; +} + +if ($found_core) { + if ($core_size_good) { + print "# Perl: OK! Found the core file and it's small!\n"; + } else { + print "# Perl: Failed! Found the core file but it's too big ($core_size)!\n"; + } +} else { + print "# Perl: Failed! Didn't find the core file!\n"; +} + +EOF + +--echo # Make server restart +--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect + +--enable_reconnect + +--echo # Wait for server to be back online +--source include/wait_until_connected_again.inc + +--disable_reconnect diff --git a/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool.result b/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool.result new file mode 100644 index 0000000000000..bf217caf8e4ea --- /dev/null +++ b/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool.result @@ -0,0 +1,13 @@ +# Shutdown server +# Restart server with --log-error +SHOW VARIABLES LIKE '%core%'; +Variable_name Value +core_file ON +innodb_buffer_pool_in_core_file OFF +# Get the full path name of the PID file +# Expecting a "crash", but don't restart the server until it is told to +# Expected max core size is 1024 MB +# Perl: Sent a SIGABRT to mysqld to dump a core. +# Perl: OK! Found the core file and it's small! +# Make server restart +# Wait for server to be back online diff --git a/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool_dynamic.result b/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool_dynamic.result new file mode 100644 index 0000000000000..f52b3063dc48f --- /dev/null +++ b/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool_dynamic.result @@ -0,0 +1,28 @@ +# Shutdown server +# Restart server with --log-error +SELECT @@global.innodb_buffer_pool_in_core_file; +@@global.innodb_buffer_pool_in_core_file +1 +SET GLOBAL innodb_buffer_pool_in_core_file = OFF; +SELECT @@global.innodb_buffer_pool_in_core_file; +@@global.innodb_buffer_pool_in_core_file +0 +SET GLOBAL innodb_buffer_pool_in_core_file = ON; +SELECT @@global.innodb_buffer_pool_in_core_file; +@@global.innodb_buffer_pool_in_core_file +1 +SET GLOBAL innodb_buffer_pool_in_core_file = OFF; +SELECT @@global.innodb_buffer_pool_in_core_file; +@@global.innodb_buffer_pool_in_core_file +0 +SHOW VARIABLES LIKE '%core%'; +Variable_name Value +core_file ON +innodb_buffer_pool_in_core_file OFF +# Get the full path name of the PID file +# Expecting a "crash", but don't restart the server until it is told to +# Expected max core size is 1024 MB +# Perl: Sent a SIGABRT to mysqld to dump a core. +# Perl: OK! Found the core file and it's small! +# Make server restart +# Wait for server to be back online diff --git a/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool_with_resizing.result b/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool_with_resizing.result new file mode 100644 index 0000000000000..952da99556201 --- /dev/null +++ b/mysql-test/suite/innodb/r/mysqld_core_dump_without_buffer_pool_with_resizing.result @@ -0,0 +1,15 @@ +# Shutdown server +# Restart server with --log-error +set global innodb_buffer_pool_size = 512*1024*1024; +set global innodb_buffer_pool_size = 2048*1024*1024; +SHOW VARIABLES LIKE '%core%'; +Variable_name Value +core_file ON +innodb_buffer_pool_in_core_file OFF +# Get the full path name of the PID file +# Expecting a "crash", but don't restart the server until it is told to +# Expected max core size is 1024 MB +# Perl: Sent a SIGABRT to mysqld to dump a core. +# Perl: OK! Found the core file and it's small! +# Make server restart +# Wait for server to be back online diff --git a/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool-master.opt b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool-master.opt new file mode 100644 index 0000000000000..11a02ab47cc8e --- /dev/null +++ b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool-master.opt @@ -0,0 +1,3 @@ +--core-file +--skip-innodb-buffer-pool-in-core-file +--innodb-buffer-pool-size=1G diff --git a/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool.test b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool.test new file mode 100644 index 0000000000000..3d32d63b0afcb --- /dev/null +++ b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool.test @@ -0,0 +1,29 @@ +################################################################################ +# This test is to test if mysqld can dump a core without large memory buffers. +# See opt file for the config: +# (1) --skip-innodb-buffer-pool-in-core-file is set +# (2) the buffer pool is set to be 1G so that with the large +# memory buffers the core size would be much greater than 1GB (the actual +# core size observed is ~1.8GB) and without them is less then 1GB (the +# actual observed size is ~700MB) + +# madvise MADV_DONTDUMP is non-posix extension available in Linux 3.4 +--let $minimum_required_linux_version = 3.4 +--source include/linux-version.inc +--source include/have_innodb.inc +--source include/not_valgrind.inc + +# Embedded mode doesn't support restart +--source include/not_embedded.inc + +--echo # Shutdown server +--source include/shutdown_mysqld.inc + +--echo # Restart server with --log-error +--exec echo "restart:--log-error=$MYSQLTEST_VARDIR/log/core_dump.err" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +SHOW VARIABLES LIKE '%core%'; +--let $expected_max_core_size = 1024 +--source include/mysqld_core_dump.inc diff --git a/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_dynamic-master.opt b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_dynamic-master.opt new file mode 100644 index 0000000000000..dcb84989bfc4d --- /dev/null +++ b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_dynamic-master.opt @@ -0,0 +1,3 @@ +--core-file +--innodb-buffer-pool-in-core-file +--innodb-buffer-pool-size=1G diff --git a/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_dynamic.test b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_dynamic.test new file mode 100644 index 0000000000000..2cd008bfc52c5 --- /dev/null +++ b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_dynamic.test @@ -0,0 +1,42 @@ +################################################################################ +# This test is to test if mysqld can dump a core without large memory buffers. +# We start the server with --innodb-buffer-pool-in-core-file ON, then +# we dynamically set it to OFF. +# Just to play with possible race conditions we switch it to ON and OFF one +# more time. +# Finally we cause core dump to happen. +# See opt file for the config: +# (1) --innodb-buffer-pool-in-core-file is set to ON initially +# (2) the buffer pool is set to be 1G so that with the large +# memory buffers the core size would be much greater than 1GB (the actual +# core size observed is ~1.8GB) and without them is less then 1GB (the +# actual observed size is ~700MB) + +# madvise MADV_DONTDUMP is non-posix extension available in Linux 3.4 +--let $minimum_required_linux_version = 3.4 +--source include/linux-version.inc +--source include/have_innodb.inc +--source include/not_valgrind.inc + +# Embedded mode doesn't support restart +--source include/not_embedded.inc + +--echo # Shutdown server +--source include/shutdown_mysqld.inc + +--echo # Restart server with --log-error +--exec echo "restart:--log-error=$MYSQLTEST_VARDIR/log/core_dump.err" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +SELECT @@global.innodb_buffer_pool_in_core_file; +SET GLOBAL innodb_buffer_pool_in_core_file = OFF; +SELECT @@global.innodb_buffer_pool_in_core_file; +SET GLOBAL innodb_buffer_pool_in_core_file = ON; +SELECT @@global.innodb_buffer_pool_in_core_file; +SET GLOBAL innodb_buffer_pool_in_core_file = OFF; +SELECT @@global.innodb_buffer_pool_in_core_file; +SHOW VARIABLES LIKE '%core%'; + +--let $expected_max_core_size = 1024 +--source include/mysqld_core_dump.inc diff --git a/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_with_resizing-master.opt b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_with_resizing-master.opt new file mode 100644 index 0000000000000..d7a46488655b2 --- /dev/null +++ b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_with_resizing-master.opt @@ -0,0 +1,5 @@ +--core-file +--skip-innodb-buffer-pool-in-core-file +--innodb-buffer-pool-size_auto_min=256M +--innodb-buffer-pool-size=1280M +--innodb-buffer-pool-size_max=2G diff --git a/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_with_resizing.test b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_with_resizing.test new file mode 100644 index 0000000000000..944c3480a9c2f --- /dev/null +++ b/mysql-test/suite/innodb/t/mysqld_core_dump_without_buffer_pool_with_resizing.test @@ -0,0 +1,45 @@ +################################################################################ +# This test is to test if mysqld can dump a core without large memory buffers. +# See opt file for the config: +# (1) --skip-innodb-buffer-pool-in-core-file is set +# (2) the buffer pool is set to be 1280MB initially, shrink it to 1024MB, then +# expand it back to 2048MB, then back to original 1280MB. +# With the large memory buffers the core size will be much greater than 1GB +# (the actual observed size is ~2.2.G) and without them it will be smaller +# than 1GB (actually ~770MB) + +# madvise MADV_DONTDUMP is non-posix extension available in Linux 3.4 +--let $minimum_required_linux_version = 3.4 +--source include/linux-version.inc +--source include/have_innodb.inc +--source include/not_valgrind.inc +# Embedded mode doesn't support restart +--source include/not_embedded.inc + +--echo # Shutdown server +--source include/shutdown_mysqld.inc + +--echo # Restart server with --log-error +--exec echo "restart:--log-error=$MYSQLTEST_VARDIR/log/core_dump_with_resizing.err" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +--enable_reconnect +--source include/wait_until_connected_again.inc + +--disable_query_log +set @old_innodb_buffer_pool_size = @@innodb_buffer_pool_size; +--enable_query_log + +# Shrink buffer pool to 500MB +set global innodb_buffer_pool_size = 512*1024*1024; + +# Expand buffer pool back to 2GB +set global innodb_buffer_pool_size = 2048*1024*1024; + +# Resize buffer pool back to original 1280MB +--disable_query_log +set global innodb_buffer_pool_size = @old_innodb_buffer_pool_size; +--enable_query_log + +SHOW VARIABLES LIKE '%core%'; + +--let $expected_max_core_size = 1024 +--source include/mysqld_core_dump.inc diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index 4f0ac6a53897b..b0cb13adce803 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -164,6 +164,18 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST NULL READ_ONLY YES COMMAND_LINE_ARGUMENT REQUIRED +VARIABLE_NAME INNODB_BUFFER_POOL_IN_CORE_FILE +SESSION_VALUE NULL +DEFAULT_VALUE ON +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE BOOLEAN +VARIABLE_COMMENT This option has no effect if @@core_file is OFF. If @@core_file is ON, and this option is OFF, then the core dump file will be generated only if it is possible to exclude buffer pool from it. As soon as it will be determined that such exclusion is impossible a warning will be emitted and @@core_file will be set to OFF to prevent generating a core dump. This option is disabled by default if the build is not a debug build and platform supports MADV_DONTDUMP. +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST OFF,ON +READ_ONLY NO +COMMAND_LINE_ARGUMENT NONE VARIABLE_NAME INNODB_BUFFER_POOL_LOAD_ABORT SESSION_VALUE NULL DEFAULT_VALUE OFF diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 322f93357bd3a..a4759030df938 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -1141,6 +1141,52 @@ int buf_pool_t::madvise_do_dump() noexcept } #endif +bool buf_pool_t::madvise_dont_dump() noexcept +{ +#if defined(HAVE_MADVISE) && defined(MADV_DODUMP) + if (madvise(memory_unaligned, size_unaligned, MADV_DONTDUMP)) + { + sql_print_warning("Disabling @@core_file because can't satisfy the value" + " of @@innodb_buffer_pool_in_core_file=OFF"); + return false; + } + return true; +#else + sql_print_warning("Disabling @@core_file because MADV_DODUMP is not supported."); + return false; +#endif +} + +bool buf_pool_t::madvise_dump() noexcept +{ +#if defined(HAVE_MADVISE) && defined(MADV_DODUMP) + if (madvise(memory_unaligned, size_unaligned, MADV_DODUMP)) { + return false; + } + return true; +#else + return false; +#endif +} + +void buf_pool_t::madvise_update_dump(bool force) +{ + mysql_mutex_lock(&mutex); + auto should_madvise_dont_dump= innobase_should_madvise_buf_pool(); + if (force + || should_madvise_dont_dump != buf_pool_should_madvise_dont_dump) + { + buf_pool_should_madvise_dont_dump= should_madvise_dont_dump; + bool success= buf_pool_should_madvise_dont_dump + ? madvise_dont_dump() : madvise_dump(); + if (buf_pool_should_madvise_dont_dump && !success) + { + innobase_disable_core_dump(); + } + } + mysql_mutex_unlock(&mutex); +} + #ifndef UNIV_DEBUG static inline byte hex_to_ascii(byte hex_digit) noexcept { @@ -1310,6 +1356,8 @@ bool buf_pool_t::create() noexcept allocated before innodb initialization */ ut_ad(srv_operation >= SRV_OPERATION_RESTORE || !field_ref_zero); + buf_pool_should_madvise_dont_dump = innobase_should_madvise_buf_pool(); + if (!field_ref_zero) { if (auto b= aligned_malloc(UNIV_PAGE_SIZE_MAX, 4096)) @@ -1359,9 +1407,14 @@ bool buf_pool_t::create() noexcept } MEM_UNDEFINED(memory_unaligned, size); - ut_dontdump(memory_unaligned, size, true); + memory= memory_unaligned + alignment_waste; size_unaligned= size; + if(buf_pool_should_madvise_dont_dump && !madvise_dont_dump()) + { + innobase_disable_core_dump(); + } + size-= alignment_waste; size&= ~(innodb_buffer_pool_extent_size - 1); @@ -1547,7 +1600,8 @@ void buf_pool_t::close() noexcept block->page.lock.free(); } - ut_dodump(memory_unaligned, size_unaligned); + if (buf_pool_should_madvise_dont_dump) madvise_dump(); + #ifdef UNIV_PFS_MEMORY PSI_MEMORY_CALL(memory_free)(mem_key_buf_buf_pool, size, owner); owner= nullptr; @@ -2121,6 +2175,7 @@ ATTRIBUTE_COLD void buf_pool_t::resize(size_t size, THD *thd) noexcept mysql_mutex_lock(&LOCK_global_system_variables); } + madvise_update_dump(true); ut_d(validate()); } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index d90038d44a9e1..41a60fb72c53a 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -1741,6 +1741,23 @@ server_get_hostname() return(glob_hostname); } +/** Checks sys_vars and determines if allocator should mark +large memory segments with MADV_DONTDUMP +@return true iff @@global.core_file AND +NOT @@global.innodb_buffer_pool_in_core_file */ +bool innobase_should_madvise_buf_pool() +{ + return (test_flags & TEST_CORE_ON_SIGNAL) && !srv_buffer_pool_in_core_file; +} + +/** Make sure that core file will not be generated, as generating a core file +might violate our promise to not dump buffer pool data, and/or might dump not +the expected memory pages due to failure in using madvise */ +void innobase_disable_core_dump() +{ + test_flags &= ~TEST_CORE_ON_SIGNAL; +} + /******************************************************************//** Returns the lock wait timeout for the current connection. @return the lock wait timeout, in seconds */ @@ -18717,6 +18734,18 @@ innodb_encrypt_tables_update(THD*, st_mysql_sys_var*, void*, const void* save) mysql_mutex_lock(&LOCK_global_system_variables); } +/** Update the system variable innodb_buffer_pool_in_core_file. +@param[in] save to-be-assigned value */ +static +void +innodb_srv_buffer_pool_in_core_file_update(THD*, st_mysql_sys_var*, void*, const void* save) +{ + mysql_mutex_unlock(&LOCK_global_system_variables); + srv_buffer_pool_in_core_file = !!(*(bool *)save); + buf_pool.madvise_update_dump(); + mysql_mutex_lock(&LOCK_global_system_variables); +} + static SHOW_VAR innodb_status_variables_export[]= { SHOW_FUNC_ENTRY("Innodb", &show_innodb_vars), {NullS, NullS, SHOW_LONG} @@ -19231,6 +19260,24 @@ static MYSQL_SYSVAR_BOOL(buffer_pool_dump_at_shutdown, srv_buffer_pool_dump_at_s "Dump the buffer pool into a file named @@innodb_buffer_pool_filename", NULL, NULL, TRUE); +#if defined(DBUG_OFF) && defined(HAVE_MADVISE) && defined(MADV_DODUMP) +#define BP_IN_CORE_DEFAULT false +#else +#define BP_IN_CORE_DEFAULT true +#endif + +static MYSQL_SYSVAR_BOOL( + buffer_pool_in_core_file , srv_buffer_pool_in_core_file, PLUGIN_VAR_NOCMDARG, + "This option has no effect if @@core_file is OFF. " + "If @@core_file is ON, and this option is OFF, then the core dump file will" + " be generated only if it is possible to exclude buffer pool from it. " + "As soon as it will be determined that such exclusion is impossible a " + "warning will be emitted and @@core_file will be set to OFF to prevent " + "generating a core dump. " + "This option is disabled by default if the build is not a debug build and" + " platform supports MADV_DONTDUMP.", + NULL, innodb_srv_buffer_pool_in_core_file_update, BP_IN_CORE_DEFAULT); + static MYSQL_SYSVAR_ULONG(buffer_pool_dump_pct, srv_buf_pool_dump_pct, PLUGIN_VAR_RQCMDARG, "Dump only the hottest N% of each buffer pool, defaults to 25", @@ -19925,6 +19972,7 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(buffer_pool_filename), MYSQL_SYSVAR(buffer_pool_dump_now), MYSQL_SYSVAR(buffer_pool_dump_at_shutdown), + MYSQL_SYSVAR(buffer_pool_in_core_file), MYSQL_SYSVAR(buffer_pool_dump_pct), #ifdef UNIV_DEBUG MYSQL_SYSVAR(buffer_pool_evict), diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 202771fcfe142..8b7fcc8c3bbf6 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -1212,6 +1212,18 @@ class buf_pool_t /** The maximum allowed innodb_buffer_pool_size */ size_t size_in_bytes_max; + /** We maintain our private view of innobase_should_madvise_buf_pool() which we + initialize at the beginning of buf_pool_t::create() and then update when the + @@global.innodb_buffer_pool_in_core_file changes. + + Changes to buf_pool_should_madvise_dont_dump are protected by buf_pool_t::mutex. + + The function buf_pool_t::madvise_update_dump() handles updating + buf_pool_should_madvise_dont_dump in reaction to changes to + @@global.innodb_buffer_pool_in_core_file. + */ + bool buf_pool_should_madvise_dont_dump = true; + /** @return the current size of the buffer pool, in bytes */ size_t curr_pool_size() const noexcept { return size_in_bytes; } @@ -1238,6 +1250,31 @@ class buf_pool_t static int madvise_do_dump() noexcept; #endif + /** Advise the OS to exclude the buffer pool from core dumps + using MADV_DONTDUMP. + + Emits a warning if unsupported or if madvise() fails. + + @retval true Advice applied successfully. + @retval false Unsupported platform or madvise() failure. */ + bool madvise_dont_dump() noexcept; + + /** Advise the OS to include the buffer pool in core dumps + using MADV_DODUMP. + + @retval true Advice applied successfully. + @retval false Unsupported platform or madvise() failure. */ + bool madvise_dump() noexcept; + + /** Re-evaluate whether the buffer pool should be excluded from + core dumps and update the madvise state if needed. + + Calls madvise_dont_dump() or madvise_dump() when the desired + state changes or when @p force is true. + + @param force Re-apply advice even if state is unchanged. */ + void madvise_update_dump(bool force = false); + /** Hash cell chain in page_hash_table */ struct hash_chain { diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index 26a498e749c13..7647ea3a9457f 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -197,6 +197,19 @@ innobase_get_at_most_n_mbchars( @retval NULL if innodb_tmpdir="" */ const char *thd_innodb_tmpdir(THD *thd); + +/** Checks sys_vars and determines if allocator should mark +large memory segments with MADV_DONTDUMP +@return true iff @@global.core_file AND +NOT @@global.innodb_buffer_pool_in_core_file */ +bool innobase_should_madvise_buf_pool(); + +/** Make sure that core file will not be generated, as generating a core file +might violate our promise to not dump buffer pool data, and/or might dump not +the expected memory pages due to failure in using madvise */ +void innobase_disable_core_dump(); + + /******************************************************************//** Returns the lock wait timeout for the current connection. @return the lock wait timeout, in seconds */ diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index fafb6218002bd..72e3a058cab79 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -273,6 +273,7 @@ extern ulong srv_innodb_stats_method; extern ulint srv_max_n_open_files; +extern my_bool srv_buffer_pool_in_core_file; extern double srv_max_buf_pool_modified_pct; extern double srv_max_dirty_pages_pct_lwm; diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index f02f872312c2f..9a2f876b502ae 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -221,6 +221,9 @@ ulong srv_io_capacity; /** innodb_io_capacity_max */ ulong srv_max_io_capacity; +/** innodb_buffer_pool_in_core_file */ +my_bool srv_buffer_pool_in_core_file; + /* The InnoDB main thread tries to keep the ratio of modified pages in the buffer pool to all database pages in the buffer pool smaller than the following number. But it is not guaranteed that the value stays below