Commit dccc6b10 authored by unknown's avatar unknown

Backport from 5.1

 -Add support for detecting version and features from mysqld binary
 - Autodetect netware
 - Disable some features not available below 5.0
 - Cleanup executable_setup to look for one executable at a time, only llok for the ones that are needed based on the selected testcases and settings


mysql-test/lib/mtr_cases.pl:
  Backport from 5.1
mysql-test/lib/mtr_io.pl:
  Backport from 5.1
  Add new function mtr_appendfile_to_file
mysql-test/lib/mtr_misc.pl:
  Backport from 5.1
mysql-test/lib/mtr_process.pl:
  Backport from 5.1
mysql-test/lib/mtr_report.pl:
  Backport from 5.1
mysql-test/mysql-test-run.pl:
  Add support for detecting version and features from mysqld binary
  Autodetect netware
  Disable some features not available below 5.0
  Cleanup executable_setup to look for one executable at a time, only llok for the ones that are needed based on the selected testcases and settings
mysql-test/r/mysqltest.result:
  Update result
mysql-test/lib/mtr_im.pl:
  New BitKeeper file ``mysql-test/lib/mtr_im.pl''
mysql-test/lib/mtr_stress.pl:
  New BitKeeper file ``mysql-test/lib/mtr_stress.pl''
parent 99cfe38a
...@@ -5,10 +5,13 @@ ...@@ -5,10 +5,13 @@
# same name. # same name.
use File::Basename; use File::Basename;
use IO::File();
use strict; use strict;
sub collect_test_cases ($); sub collect_test_cases ($);
sub collect_one_test_case ($$$$$$); sub collect_one_test_case ($$$$$$$);
sub mtr_options_from_test_file($$);
############################################################################## ##############################################################################
# #
...@@ -37,21 +40,6 @@ sub collect_test_cases ($) { ...@@ -37,21 +40,6 @@ sub collect_test_cases ($) {
opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!"); opendir(TESTDIR, $testdir) or mtr_error("Can't open dir \"$testdir\": $!");
if ( @::opt_cases )
{
foreach my $tname ( @::opt_cases ) { # Run in specified order, no sort
$tname= basename($tname, ".test");
my $elem= "$tname.test";
if ( ! -f "$testdir/$elem")
{
mtr_error("Test case $tname ($testdir/$elem) is not found");
}
collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,{});
}
closedir TESTDIR;
}
else
{
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Disable some tests listed in disabled.def # Disable some tests listed in disabled.def
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
...@@ -69,49 +57,138 @@ sub collect_test_cases ($) { ...@@ -69,49 +57,138 @@ sub collect_test_cases ($) {
close DISABLED; close DISABLED;
} }
foreach my $elem ( sort readdir(TESTDIR) ) { if ( @::opt_cases )
my $tname= mtr_match_extension($elem,"test"); {
next if ! defined $tname; foreach my $tname ( @::opt_cases ) { # Run in specified order, no sort
next if $::opt_do_test and ! defined mtr_match_prefix($elem,$::opt_do_test); my $elem= undef;
my $component_id= undef;
# Get rid of directory part (path). Leave the extension since it is used
# to understand type of the test.
$tname = basename($tname);
collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled); # Check if the extenstion has been specified.
if ( mtr_match_extension($tname, "test") )
{
$elem= $tname;
$tname=~ s/\.test$//;
$component_id= 'mysqld';
} }
closedir TESTDIR; elsif ( mtr_match_extension($tname, "imtest") )
{
$elem= $tname;
$tname =~ s/\.imtest$//;
$component_id= 'im';
} }
# To speed things up, we sort first in if the test require a restart # If target component is known, check that the specified test case
# or not, second in alphanumeric order. # exists.
#
# Otherwise, try to guess the target component.
if ( $::opt_reorder ) if ( $component_id )
{ {
@$cases = sort { if ( ! -f "$testdir/$elem")
if ( ! $a->{'master_restart'} and ! $b->{'master_restart'} )
{ {
return $a->{'name'} cmp $b->{'name'}; mtr_error("Test case $tname ($testdir/$elem) is not found");
}
} }
else
{
my $mysqld_test_exists = -f "$testdir/$tname.test";
my $im_test_exists = -f "$testdir/$tname.imtest";
if ( $a->{'master_restart'} and $b->{'master_restart'} ) if ( $mysqld_test_exists and $im_test_exists )
{ {
my $cmp= mtr_cmp_opts($a->{'master_opt'}, $b->{'master_opt'}); mtr_error("Ambiguous test case name ($tname)");
if ( $cmp == 0 ) }
elsif ( ! $mysqld_test_exists and ! $im_test_exists )
{ {
return $a->{'name'} cmp $b->{'name'}; mtr_error("Test case $tname is not found");
} }
else elsif ( $mysqld_test_exists )
{
$elem= "$tname.test";
$component_id= 'mysqld';
}
elsif ( $im_test_exists )
{ {
return $cmp; $elem= "$tname.imtest";
$component_id= 'im';
} }
} }
if ( $a->{'master_restart'} ) collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled,
$component_id);
}
closedir TESTDIR;
}
else
{ {
return 1; # Is greater foreach my $elem ( sort readdir(TESTDIR) ) {
my $component_id= undef;
my $tname= undef;
if ($tname= mtr_match_extension($elem, 'test'))
{
$component_id = 'mysqld';
}
elsif ($tname= mtr_match_extension($elem, 'imtest'))
{
$component_id = 'im';
} }
else else
{ {
return -1; # Is less next;
} }
} @$cases;
next if $::opt_do_test and ! defined mtr_match_prefix($elem,$::opt_do_test);
collect_one_test_case($testdir,$resdir,$tname,$elem,$cases,\%disabled,
$component_id);
}
closedir TESTDIR;
}
# To speed things up, we sort first in if the test require a restart
# or not, second in alphanumeric order.
if ( $::opt_reorder )
{
my %sort_criteria;
my $tinfo;
# Make a mapping of test name to a string that represents how that test
# should be sorted among the other tests. Put the most important criterion
# first, then a sub-criterion, then sub-sub-criterion, et c.
foreach $tinfo (@$cases)
{
my @this_criteria = ();
# Append the criteria for sorting, in order of importance.
push(@this_criteria, join("!", sort @{$tinfo->{'master_opt'}}) . "~"); # Ending with "~" makes empty sort later than filled
push(@this_criteria, "ndb=" . ($tinfo->{'ndb_test'} ? "1" : "0"));
push(@this_criteria, "restart=" . ($tinfo->{'master_restart'} ? "1" : "0"));
push(@this_criteria, "big_test=" . ($tinfo->{'big_test'} ? "1" : "0"));
push(@this_criteria, join("|", sort keys %{$tinfo})); # Group similar things together. The values may differ substantially. FIXME?
push(@this_criteria, $tinfo->{'name'}); # Finally, order by the name
$sort_criteria{$tinfo->{"name"}} = join(" ", @this_criteria);
}
@$cases = sort { $sort_criteria{$a->{"name"}} cmp $sort_criteria{$b->{"name"}}; } @$cases;
### For debugging the sort-order
# foreach $tinfo (@$cases)
# {
# print $sort_criteria{$tinfo->{"name"}};
# print " -> \t";
# print $tinfo->{"name"};
# print "\n";
# }
} }
return $cases; return $cases;
...@@ -125,13 +202,14 @@ sub collect_test_cases ($) { ...@@ -125,13 +202,14 @@ sub collect_test_cases ($) {
############################################################################## ##############################################################################
sub collect_one_test_case($$$$$$) { sub collect_one_test_case($$$$$$$) {
my $testdir= shift; my $testdir= shift;
my $resdir= shift; my $resdir= shift;
my $tname= shift; my $tname= shift;
my $elem= shift; my $elem= shift;
my $cases= shift; my $cases= shift;
my $disabled=shift; my $disabled=shift;
my $component_id= shift;
my $path= "$testdir/$elem"; my $path= "$testdir/$elem";
...@@ -151,6 +229,7 @@ sub collect_one_test_case($$$$$$) { ...@@ -151,6 +229,7 @@ sub collect_one_test_case($$$$$$) {
my $tinfo= {}; my $tinfo= {};
$tinfo->{'name'}= $tname; $tinfo->{'name'}= $tname;
$tinfo->{'result_file'}= "$resdir/$tname.result"; $tinfo->{'result_file'}= "$resdir/$tname.result";
$tinfo->{'component_id'} = $component_id;
push(@$cases, $tinfo); push(@$cases, $tinfo);
if ( $::opt_skip_test and defined mtr_match_prefix($tname,$::opt_skip_test) ) if ( $::opt_skip_test and defined mtr_match_prefix($tname,$::opt_skip_test) )
...@@ -171,48 +250,54 @@ sub collect_one_test_case($$$$$$) { ...@@ -171,48 +250,54 @@ sub collect_one_test_case($$$$$$) {
if ( $::opt_skip_rpl ) if ( $::opt_skip_rpl )
{ {
$tinfo->{'skip'}= 1; $tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "No replication tests(--skip-rpl)";
return; return;
} }
$tinfo->{'slave_num'}= 1; # Default, use one slave $tinfo->{'slave_num'}= 1; # Default, use one slave
# FIXME currently we always restart slaves
$tinfo->{'slave_restart'}= 1;
if ( $tname eq 'rpl_failsafe' or $tname eq 'rpl_chain_temp_table' ) if ( $tname eq 'rpl_failsafe' or $tname eq 'rpl_chain_temp_table' )
{ {
# $tinfo->{'slave_num'}= 3; # Not 3 ? Check old code, strange # $tinfo->{'slave_num'}= 3; # Not 3 ? Check old code, strange
} }
} }
if ( defined mtr_match_prefix($tname,"federated") ) if ( defined mtr_match_prefix($tname,"federated") )
{ {
$tinfo->{'slave_num'}= 1; # Default, use one slave # Default, federated uses the first slave as it's federated database
$tinfo->{'slave_num'}= 1;
# FIXME currently we always restart slaves
$tinfo->{'slave_restart'}= 1;
} }
# Cluster is needed by test case if testname contains ndb if ( $::opt_with_ndbcluster or defined mtr_match_substring($tname,"ndb") )
if ( defined mtr_match_substring($tname,"ndb") )
{ {
# This is an ndb test or all tests should be run with ndb cluster started
$tinfo->{'ndb_test'}= 1; $tinfo->{'ndb_test'}= 1;
if ( $::opt_skip_ndbcluster ) if ( $::opt_skip_ndbcluster )
{ {
# Skip all ndb tests # All ndb test's should be skipped
$tinfo->{'skip'}= 1; $tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "No ndbcluster test(--skip-ndbcluster)";
return; return;
} }
if ( ! $::opt_with_ndbcluster ) if ( ! $::opt_ndbcluster_supported )
{ {
# Ndb is not supported, skip them # Ndb is not supported, skip them
$tinfo->{'skip'}= 1; $tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "No ndbcluster support";
return; return;
} }
} }
else else
{ {
# This is not a ndb test
$tinfo->{'ndb_test'}= 0; $tinfo->{'ndb_test'}= 0;
if ( $::opt_with_ndbcluster_only )
{
# Only the ndb test should be run, all other should be skipped
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "Only ndbcluster tests(--with-ndbcluster-only)";
return;
}
} }
# FIXME what about embedded_server + ndbcluster, skip ?! # FIXME what about embedded_server + ndbcluster, skip ?!
...@@ -223,6 +308,7 @@ sub collect_one_test_case($$$$$$) { ...@@ -223,6 +308,7 @@ sub collect_one_test_case($$$$$$) {
my $master_sh= "$testdir/$tname-master.sh"; my $master_sh= "$testdir/$tname-master.sh";
my $slave_sh= "$testdir/$tname-slave.sh"; my $slave_sh= "$testdir/$tname-slave.sh";
my $disabled_file= "$testdir/$tname.disabled"; my $disabled_file= "$testdir/$tname.disabled";
my $im_opt_file= "$testdir/$tname-im.opt";
$tinfo->{'master_opt'}= []; $tinfo->{'master_opt'}= [];
$tinfo->{'slave_opt'}= []; $tinfo->{'slave_opt'}= [];
...@@ -303,6 +389,8 @@ sub collect_one_test_case($$$$$$) { ...@@ -303,6 +389,8 @@ sub collect_one_test_case($$$$$$) {
if ( $::glob_win32_perl ) if ( $::glob_win32_perl )
{ {
$tinfo->{'skip'}= 1; $tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "No tests with sh scripts on Windows";
return;
} }
else else
{ {
...@@ -316,6 +404,8 @@ sub collect_one_test_case($$$$$$) { ...@@ -316,6 +404,8 @@ sub collect_one_test_case($$$$$$) {
if ( $::glob_win32_perl ) if ( $::glob_win32_perl )
{ {
$tinfo->{'skip'}= 1; $tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "No tests with sh scripts on Windows";
return;
} }
else else
{ {
...@@ -324,19 +414,106 @@ sub collect_one_test_case($$$$$$) { ...@@ -324,19 +414,106 @@ sub collect_one_test_case($$$$$$) {
} }
} }
if ( -f $im_opt_file )
{
$tinfo->{'im_opts'} = mtr_get_opts_from_file($im_opt_file);
}
else
{
$tinfo->{'im_opts'} = [];
}
# FIXME why this late? # FIXME why this late?
my $marked_as_disabled= 0;
if ( $disabled->{$tname} ) if ( $disabled->{$tname} )
{ {
$tinfo->{'skip'}= 1; $marked_as_disabled= 1;
$tinfo->{'disable'}= 1; # Sub type of 'skip' $tinfo->{'comment'}= $disabled->{$tname};
$tinfo->{'comment'}= $disabled->{$tname} if $disabled->{$tname};
} }
if ( -f $disabled_file ) if ( -f $disabled_file )
{
$marked_as_disabled= 1;
$tinfo->{'comment'}= mtr_fromfile($disabled_file);
}
# If test was marked as disabled, either opt_enable_disabled is off and then
# we skip this test, or it is on and then we run this test but warn
if ( $marked_as_disabled )
{
if ( $::opt_enable_disabled )
{
$tinfo->{'dont_skip_though_disabled'}= 1;
}
else
{ {
$tinfo->{'skip'}= 1; $tinfo->{'skip'}= 1;
$tinfo->{'disable'}= 1; # Sub type of 'skip' $tinfo->{'disable'}= 1; # Sub type of 'skip'
$tinfo->{'comment'}= mtr_fromfile($disabled_file); return;
}
}
if ( $component_id eq 'im' )
{
if ( $::glob_use_embedded_server )
{
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "No IM with embedded server";
return;
}
elsif ( $::opt_ps_protocol )
{
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "No IM with --ps-protocol";
return;
}
elsif ( $::opt_skip_im )
{
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "No IM tests(--skip-im)";
return;
}
}
else
{
mtr_options_from_test_file($tinfo,"$testdir/${tname}.test");
if ( $tinfo->{'big_test'} and ! $::opt_big_test )
{
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "Test need 'big-test' option";
return;
}
if ( $tinfo->{'ndb_extra'} and ! $::opt_ndb_extra_test )
{
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "Test need 'ndb_extra' option";
return;
}
if ( $tinfo->{'require_manager'} )
{
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "Test need the _old_ manager(to be removed)";
return;
}
if ( defined $tinfo->{'binlog_format'} and
! ( $tinfo->{'binlog_format'} eq $::used_binlog_format ) )
{
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "Not running with binlog format '$tinfo->{'binlog_format'}'";
return;
}
if ( $tinfo->{'need_debug'} && ! $::debug_compiled_binaries )
{
$tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "Test need debug binaries";
return;
}
} }
# We can't restart a running server that may be in use # We can't restart a running server that may be in use
...@@ -345,8 +522,58 @@ sub collect_one_test_case($$$$$$) { ...@@ -345,8 +522,58 @@ sub collect_one_test_case($$$$$$) {
( $tinfo->{'master_restart'} or $tinfo->{'slave_restart'} ) ) ( $tinfo->{'master_restart'} or $tinfo->{'slave_restart'} ) )
{ {
$tinfo->{'skip'}= 1; $tinfo->{'skip'}= 1;
$tinfo->{'comment'}= "Can't restart a running server";
return;
} }
} }
# List of tags in the .test files that if found should set
# the specified value in "tinfo"
our @tags=
(
["include/have_innodb.inc", "innodb_test", 1],
["include/have_binlog_format_row.inc", "binlog_format", "row"],
["include/have_binlog_format_statement.inc", "binlog_format", "stmt"],
["include/big_test.inc", "big_test", 1],
["include/have_debug.inc", "need_debug", 1],
["include/have_ndb_extra.inc", "ndb_extra", 1],
["require_manager", "require_manager", 1],
);
sub mtr_options_from_test_file($$) {
my $tinfo= shift;
my $file= shift;
#mtr_verbose("$file");
my $F= IO::File->new($file) or mtr_error("can't open file \"$file\": $!");
while ( my $line= <$F> )
{
next if ( $line !~ /^--/ );
# Match this line against tag in "tags" array
foreach my $tag (@tags)
{
if ( index($line, $tag->[0]) >= 0 )
{
# Tag matched, assign value to "tinfo"
$tinfo->{"$tag->[1]"}= $tag->[2];
}
}
# If test sources another file, open it as well
if ( $line =~ /^\-\-([[:space:]]*)source(.*)$/ )
{
my $value= $2;
$value =~ s/^\s+//; # Remove leading space
$value =~ s/[[:space:]]+$//; # Remove ending space
my $sourced_file= "$::glob_mysql_test_dir/$value";
mtr_options_from_test_file($tinfo, $sourced_file);
}
}
}
1; 1;
# -*- cperl -*-
# This is a library file used by the Perl version of mysql-test-run,
# and is part of the translation of the Bourne shell script with the
# same name.
use strict;
# Private IM-related operations.
sub mtr_im_kill_process ($$$$);
sub mtr_im_load_pids ($);
sub mtr_im_terminate ($);
sub mtr_im_check_alive ($);
sub mtr_im_check_main_alive ($);
sub mtr_im_check_angel_alive ($);
sub mtr_im_check_mysqlds_alive ($);
sub mtr_im_check_mysqld_alive ($);
sub mtr_im_cleanup ($);
sub mtr_im_rm_file ($);
sub mtr_im_errlog ($);
sub mtr_im_kill ($);
sub mtr_im_wait_for_connection ($$$);
sub mtr_im_wait_for_mysqld($$$);
# Public IM-related operations.
sub mtr_im_start ($$);
sub mtr_im_stop ($);
##############################################################################
#
# Private operations.
#
##############################################################################
sub mtr_im_kill_process ($$$$) {
my $pid_lst= shift;
my $signal= shift;
my $total_retries= shift;
my $timeout= shift;
my %pids;
foreach my $pid ( @{$pid_lst} )
{
$pids{$pid}= 1;
}
for ( my $cur_attempt= 1; $cur_attempt <= $total_retries; ++$cur_attempt )
{
foreach my $pid ( keys %pids )
{
mtr_debug("Sending $signal to $pid...");
kill($signal, $pid);
unless ( kill (0, $pid) )
{
mtr_debug("Process $pid died.");
delete $pids{$pid};
}
}
return if scalar keys %pids == 0;
mtr_debug("Sleeping $timeout second(s) waiting for processes to die...");
sleep($timeout);
}
mtr_debug("Process(es) " .
join(' ', keys %pids) .
" is still alive after $total_retries " .
"of sending signal $signal.");
}
###########################################################################
sub mtr_im_load_pids($) {
my $im= shift;
mtr_debug("Loading PID files...");
# Obtain mysqld-process pids.
my $instances = $im->{'instances'};
for ( my $idx= 0; $idx < 2; ++$idx )
{
mtr_debug("IM-guarded mysqld[$idx] PID file: '" .
$instances->[$idx]->{'path_pid'} . "'.");
my $mysqld_pid;
if ( -r $instances->[$idx]->{'path_pid'} )
{
$mysqld_pid= mtr_get_pid_from_file($instances->[$idx]->{'path_pid'});
mtr_debug("IM-guarded mysqld[$idx] PID: $mysqld_pid.");
}
else
{
$mysqld_pid= undef;
mtr_debug("IM-guarded mysqld[$idx]: no PID file.");
}
$instances->[$idx]->{'pid'}= $mysqld_pid;
}
# Re-read Instance Manager PIDs from the file, since during tests Instance
# Manager could have been restarted, so its PIDs could have been changed.
# - IM-main
mtr_debug("IM-main PID file: '$im->{path_pid}'.");
if ( -f $im->{'path_pid'} )
{
$im->{'pid'} =
mtr_get_pid_from_file($im->{'path_pid'});
mtr_debug("IM-main PID: $im->{pid}.");
}
else
{
mtr_debug("IM-main: no PID file.");
$im->{'pid'}= undef;
}
# - IM-angel
mtr_debug("IM-angel PID file: '$im->{path_angel_pid}'.");
if ( -f $im->{'path_angel_pid'} )
{
$im->{'angel_pid'} =
mtr_get_pid_from_file($im->{'path_angel_pid'});
mtr_debug("IM-angel PID: $im->{'angel_pid'}.");
}
else
{
mtr_debug("IM-angel: no PID file.");
$im->{'angel_pid'} = undef;
}
}
###########################################################################
sub mtr_im_terminate($) {
my $im= shift;
# Load pids from pid-files. We should do it first of all, because IM deletes
# them on shutdown.
mtr_im_load_pids($im);
mtr_debug("Shutting Instance Manager down...");
# Ignoring SIGCHLD so that all children could rest in peace.
start_reap_all();
# Send SIGTERM to IM-main.
if ( defined $im->{'pid'} )
{
mtr_debug("IM-main pid: $im->{pid}.");
mtr_debug("Stopping IM-main...");
mtr_im_kill_process([ $im->{'pid'} ], 'TERM', 10, 1);
}
else
{
mtr_debug("IM-main pid: n/a.");
}
# If IM-angel was alive, wait for it to die.
if ( defined $im->{'angel_pid'} )
{
mtr_debug("IM-angel pid: $im->{'angel_pid'}.");
mtr_debug("Waiting for IM-angel to die...");
my $total_attempts= 10;
for ( my $cur_attempt=1; $cur_attempt <= $total_attempts; ++$cur_attempt )
{
unless ( kill (0, $im->{'angel_pid'}) )
{
mtr_debug("IM-angel died.");
last;
}
sleep(1);
}
}
else
{
mtr_debug("IM-angel pid: n/a.");
}
stop_reap_all();
# Re-load PIDs.
mtr_im_load_pids($im);
}
###########################################################################
sub mtr_im_check_alive($) {
my $im= shift;
mtr_debug("Checking whether IM-components are alive...");
return 1 if mtr_im_check_main_alive($im);
return 1 if mtr_im_check_angel_alive($im);
return 1 if mtr_im_check_mysqlds_alive($im);
return 0;
}
###########################################################################
sub mtr_im_check_main_alive($) {
my $im= shift;
# Check that the process, that we know to be IM's, is dead.
if ( defined $im->{'pid'} )
{
if ( kill (0, $im->{'pid'}) )
{
mtr_debug("IM-main (PID: $im->{pid}) is alive.");
return 1;
}
else
{
mtr_debug("IM-main (PID: $im->{pid}) is dead.");
}
}
else
{
mtr_debug("No PID file for IM-main.");
}
# Check that IM does not accept client connections.
if ( mtr_ping_port($im->{'port'}) )
{
mtr_debug("IM-main (port: $im->{port}) " .
"is accepting connections.");
mtr_im_errlog("IM-main is accepting connections on port " .
"$im->{port}, but there is no " .
"process information.");
return 1;
}
else
{
mtr_debug("IM-main (port: $im->{port}) " .
"does not accept connections.");
return 0;
}
}
###########################################################################
sub mtr_im_check_angel_alive($) {
my $im= shift;
# Check that the process, that we know to be the Angel, is dead.
if ( defined $im->{'angel_pid'} )
{
if ( kill (0, $im->{'angel_pid'}) )
{
mtr_debug("IM-angel (PID: $im->{angel_pid}) is alive.");
return 1;
}
else
{
mtr_debug("IM-angel (PID: $im->{angel_pid}) is dead.");
return 0;
}
}
else
{
mtr_debug("No PID file for IM-angel.");
return 0;
}
}
###########################################################################
sub mtr_im_check_mysqlds_alive($) {
my $im= shift;
mtr_debug("Checking for IM-guarded mysqld instances...");
my $instances = $im->{'instances'};
for ( my $idx= 0; $idx < 2; ++$idx )
{
mtr_debug("Checking mysqld[$idx]...");
return 1
if mtr_im_check_mysqld_alive($instances->[$idx]);
}
}
###########################################################################
sub mtr_im_check_mysqld_alive($) {
my $mysqld_instance= shift;
# Check that the process is dead.
if ( defined $mysqld_instance->{'pid'} )
{
if ( kill (0, $mysqld_instance->{'pid'}) )
{
mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is alive.");
return 1;
}
else
{
mtr_debug("Mysqld instance (PID: $mysqld_instance->{pid}) is dead.");
}
}
else
{
mtr_debug("No PID file for mysqld instance.");
}
# Check that mysqld does not accept client connections.
if ( mtr_ping_port($mysqld_instance->{'port'}) )
{
mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " .
"is accepting connections.");
mtr_im_errlog("Mysqld is accepting connections on port " .
"$mysqld_instance->{port}, but there is no " .
"process information.");
return 1;
}
else
{
mtr_debug("Mysqld instance (port: $mysqld_instance->{port}) " .
"does not accept connections.");
return 0;
}
}
###########################################################################
sub mtr_im_cleanup($) {
my $im= shift;
mtr_im_rm_file($im->{'path_pid'});
mtr_im_rm_file($im->{'path_sock'});
mtr_im_rm_file($im->{'path_angel_pid'});
for ( my $idx= 0; $idx < 2; ++$idx )
{
mtr_im_rm_file($im->{'instances'}->[$idx]->{'path_pid'});
mtr_im_rm_file($im->{'instances'}->[$idx]->{'path_sock'});
}
}
###########################################################################
sub mtr_im_rm_file($)
{
my $file_path= shift;
if ( -f $file_path )
{
mtr_debug("Removing '$file_path'...");
unless ( unlink($file_path) )
{
mtr_warning("Can not remove '$file_path'.")
}
}
else
{
mtr_debug("File '$file_path' does not exist already.");
}
}
###########################################################################
sub mtr_im_errlog($) {
my $msg= shift;
# Complain in error log so that a warning will be shown.
#
# TODO: unless BUG#20761 is fixed, we will print the warning to stdout, so
# that it can be seen on console and does not produce pushbuild error.
# my $errlog= "$opt_vardir/log/mysql-test-run.pl.err";
#
# open (ERRLOG, ">>$errlog") ||
# mtr_error("Can not open error log ($errlog)");
#
# my $ts= localtime();
# print ERRLOG
# "Warning: [$ts] $msg\n";
#
# close ERRLOG;
my $ts= localtime();
print "Warning: [$ts] $msg\n";
}
###########################################################################
sub mtr_im_kill($) {
my $im= shift;
# Re-load PIDs. That can be useful because some processes could have been
# restarted.
mtr_im_load_pids($im);
# Ignoring SIGCHLD so that all children could rest in peace.
start_reap_all();
# Kill IM-angel first of all.
if ( defined $im->{'angel_pid'} )
{
mtr_debug("Killing IM-angel (PID: $im->{angel_pid})...");
mtr_im_kill_process([ $im->{'angel_pid'} ], 'KILL', 10, 1)
}
else
{
mtr_debug("IM-angel is dead.");
}
# Re-load PIDs again.
mtr_im_load_pids($im);
# Kill IM-main.
if ( defined $im->{'pid'} )
{
mtr_debug("Killing IM-main (PID: $im->pid})...");
mtr_im_kill_process([ $im->{'pid'} ], 'KILL', 10, 1);
}
else
{
mtr_debug("IM-main is dead.");
}
# Re-load PIDs again.
mtr_im_load_pids($im);
# Kill guarded mysqld instances.
my @mysqld_pids;
mtr_debug("Collecting PIDs of mysqld instances to kill...");
for ( my $idx= 0; $idx < 2; ++$idx )
{
my $pid= $im->{'instances'}->[$idx]->{'pid'};
unless ( defined $pid )
{
next;
}
mtr_debug(" - IM-guarded mysqld[$idx] PID: $pid.");
push (@mysqld_pids, $pid);
}
if ( scalar @mysqld_pids > 0 )
{
mtr_debug("Killing IM-guarded mysqld instances...");
mtr_im_kill_process(\@mysqld_pids, 'KILL', 10, 1);
}
# That's all.
stop_reap_all();
}
##############################################################################
sub mtr_im_wait_for_connection($$$) {
my $im= shift;
my $total_attempts= shift;
my $connect_timeout= shift;
mtr_debug("Waiting for IM on port $im->{port} " .
"to start accepting connections...");
for ( my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt )
{
mtr_debug("Trying to connect to IM ($cur_attempt of $total_attempts)...");
if ( mtr_ping_port($im->{'port'}) )
{
mtr_debug("IM is accepting connections " .
"on port $im->{port}.");
return 1;
}
mtr_debug("Sleeping $connect_timeout...");
sleep($connect_timeout);
}
mtr_debug("IM does not accept connections " .
"on port $im->{port} after " .
($total_attempts * $connect_timeout) . " seconds.");
return 0;
}
##############################################################################
sub mtr_im_wait_for_mysqld($$$) {
my $mysqld= shift;
my $total_attempts= shift;
my $connect_timeout= shift;
mtr_debug("Waiting for IM-guarded mysqld on port $mysqld->{port} " .
"to start accepting connections...");
for ( my $cur_attempt= 1; $cur_attempt <= $total_attempts; ++$cur_attempt )
{
mtr_debug("Trying to connect to mysqld " .
"($cur_attempt of $total_attempts)...");
if ( mtr_ping_port($mysqld->{'port'}) )
{
mtr_debug("Mysqld is accepting connections " .
"on port $mysqld->{port}.");
return 1;
}
mtr_debug("Sleeping $connect_timeout...");
sleep($connect_timeout);
}
mtr_debug("Mysqld does not accept connections " .
"on port $mysqld->{port} after " .
($total_attempts * $connect_timeout) . " seconds.");
return 0;
}
##############################################################################
#
# Public operations.
#
##############################################################################
sub mtr_im_start($$) {
my $im = shift;
my $opts = shift;
mtr_debug("Starting Instance Manager...");
my $args;
mtr_init_args(\$args);
mtr_add_arg($args, "--defaults-file=%s", $im->{'defaults_file'});
foreach my $opt ( @{$opts} )
{
mtr_add_arg($args, $opt);
}
$im->{'pid'} =
mtr_spawn(
$::exe_im, # path to the executable
$args, # cmd-line args
'', # stdin
$im->{'path_log'}, # stdout
$im->{'path_err'}, # stderr
'', # pid file path (not used)
{ append_log_file => 1 } # append log files
);
unless ( $im->{'pid'} )
{
mtr_error('Could not start Instance Manager.')
}
# Instance Manager can be run in daemon mode. In this case, it creates
# several processes and the parent process, created by mtr_spawn(), exits just
# after start. So, we have to obtain Instance Manager PID from the PID file.
mtr_debug("Waiting for IM to create PID file (" .
"path: '$im->{path_pid}'; " .
"timeout: $im->{start_timeout})...");
unless ( sleep_until_file_created($im->{'path_pid'},
$im->{'start_timeout'},
-1) ) # real PID is still unknown
{
mtr_debug("IM has not created PID file in $im->{start_timeout} secs.");
mtr_debug("Aborting test suite...");
mtr_kill_leftovers();
mtr_report("IM has not created PID file in $im->{start_timeout} secs.");
return 0;
}
$im->{'pid'}= mtr_get_pid_from_file($im->{'path_pid'});
mtr_debug("Instance Manager started. PID: $im->{pid}.");
# Wait until we can connect to IM.
my $IM_CONNECT_TIMEOUT= 30;
unless ( mtr_im_wait_for_connection($im,
$IM_CONNECT_TIMEOUT, 1) )
{
mtr_debug("Can not connect to Instance Manager " .
"in $IM_CONNECT_TIMEOUT seconds after start.");
mtr_debug("Aborting test suite...");
mtr_kill_leftovers();
mtr_report("Can not connect to Instance Manager " .
"in $IM_CONNECT_TIMEOUT seconds after start.");
return 0;
}
# Wait for IM to start guarded instances:
# - wait for PID files;
mtr_debug("Waiting for guarded mysqlds instances to create PID files...");
for ( my $idx= 0; $idx < 2; ++$idx )
{
my $mysqld= $im->{'instances'}->[$idx];
if ( exists $mysqld->{'nonguarded'} )
{
next;
}
mtr_debug("Waiting for mysqld[$idx] to create PID file (" .
"path: '$mysqld->{path_pid}'; " .
"timeout: $mysqld->{start_timeout})...");
unless ( sleep_until_file_created($mysqld->{'path_pid'},
$mysqld->{'start_timeout'},
-1) ) # real PID is still unknown
{
mtr_debug("mysqld[$idx] has not created PID file in " .
"$mysqld->{start_timeout} secs.");
mtr_debug("Aborting test suite...");
mtr_kill_leftovers();
mtr_report("mysqld[$idx] has not created PID file in " .
"$mysqld->{start_timeout} secs.");
return 0;
}
mtr_debug("PID file for mysqld[$idx] ($mysqld->{path_pid} created.");
}
# Wait until we can connect to guarded mysqld-instances
# (in other words -- wait for IM to start guarded instances).
mtr_debug("Waiting for guarded mysqlds to start accepting connections...");
for ( my $idx= 0; $idx < 2; ++$idx )
{
my $mysqld= $im->{'instances'}->[$idx];
if ( exists $mysqld->{'nonguarded'} )
{
next;
}
mtr_debug("Waiting for mysqld[$idx] to accept connection...");
unless ( mtr_im_wait_for_mysqld($mysqld, 30, 1) )
{
mtr_debug("Can not connect to mysqld[$idx] " .
"in $IM_CONNECT_TIMEOUT seconds after start.");
mtr_debug("Aborting test suite...");
mtr_kill_leftovers();
mtr_report("Can not connect to mysqld[$idx] " .
"in $IM_CONNECT_TIMEOUT seconds after start.");
return 0;
}
mtr_debug("mysqld[$idx] started.");
}
mtr_debug("Instance Manager and its components are up and running.");
return 1;
}
##############################################################################
sub mtr_im_stop($) {
my $im= shift;
mtr_debug("Stopping Instance Manager...");
# Try graceful shutdown.
mtr_im_terminate($im);
# Check that all processes died.
unless ( mtr_im_check_alive($im) )
{
mtr_debug("Instance Manager has been stopped successfully.");
mtr_im_cleanup($im);
return 1;
}
# Instance Manager don't want to die. We should kill it.
mtr_im_errlog("Instance Manager did not shutdown gracefully.");
mtr_im_kill($im);
# Check again that all IM-related processes have been killed.
my $im_is_alive= mtr_im_check_alive($im);
mtr_im_cleanup($im);
if ( $im_is_alive )
{
mtr_debug("Can not kill Instance Manager or its children.");
return 0;
}
mtr_debug("Instance Manager has been killed successfully.");
return 1;
}
###########################################################################
1;
...@@ -11,6 +11,8 @@ sub mtr_get_opts_from_file ($); ...@@ -11,6 +11,8 @@ sub mtr_get_opts_from_file ($);
sub mtr_fromfile ($); sub mtr_fromfile ($);
sub mtr_tofile ($@); sub mtr_tofile ($@);
sub mtr_tonewfile($@); sub mtr_tonewfile($@);
sub mtr_lastlinefromfile($);
sub mtr_appendfile_to_file ($$);
############################################################################## ##############################################################################
# #
...@@ -19,13 +21,39 @@ sub mtr_tonewfile($@); ...@@ -19,13 +21,39 @@ sub mtr_tonewfile($@);
############################################################################## ##############################################################################
sub mtr_get_pid_from_file ($) { sub mtr_get_pid_from_file ($) {
my $file= shift; my $pid_file_path= shift;
my $TOTAL_ATTEMPTS= 30;
my $timeout= 1;
# We should read from the file until we get correct pid. As it is
# stated in BUG#21884, pid file can be empty at some moment. So, we should
# read it until we get valid data.
for (my $cur_attempt= 1; $cur_attempt <= $TOTAL_ATTEMPTS; ++$cur_attempt)
{
mtr_debug("Reading pid file '$pid_file_path' " .
"($cur_attempt of $TOTAL_ATTEMPTS)...");
open(FILE, '<', $pid_file_path)
or mtr_error("can't open file \"$pid_file_path\": $!");
open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!");
my $pid= <FILE>; my $pid= <FILE>;
chomp($pid);
chomp($pid) if defined $pid;
close FILE; close FILE;
return $pid;
return $pid if defined $pid && $pid ne '';
mtr_debug("Pid file '$pid_file_path' is empty. " .
"Sleeping $timeout second(s)...");
sleep(1);
}
mtr_error("Pid file '$pid_file_path' is corrupted. " .
"Can not retrieve PID in " .
($timeout * $TOTAL_ATTEMPTS) . " seconds.");
} }
sub mtr_get_opts_from_file ($) { sub mtr_get_opts_from_file ($) {
...@@ -113,6 +141,20 @@ sub mtr_fromfile ($) { ...@@ -113,6 +141,20 @@ sub mtr_fromfile ($) {
return $text; return $text;
} }
sub mtr_lastlinefromfile ($) {
my $file= shift;
my $text;
open(FILE,"<",$file) or mtr_error("can't open file \"$file\": $!");
while (my $line= <FILE>)
{
$text= $line;
}
close FILE;
return $text;
}
sub mtr_tofile ($@) { sub mtr_tofile ($@) {
my $file= shift; my $file= shift;
...@@ -129,5 +171,17 @@ sub mtr_tonewfile ($@) { ...@@ -129,5 +171,17 @@ sub mtr_tonewfile ($@) {
close FILE; close FILE;
} }
sub mtr_appendfile_to_file ($$) {
my $from_file= shift;
my $to_file= shift;
open(TOFILE,">>",$to_file) or mtr_error("can't open file \"$to_file\": $!");
open(FROMFILE,">>",$from_file)
or mtr_error("can't open file \"$from_file\": $!");
print TOFILE while (<FROMFILE>);
close FROMFILE;
close TOFILE;
}
1; 1;
...@@ -9,9 +9,10 @@ use strict; ...@@ -9,9 +9,10 @@ use strict;
sub mtr_full_hostname (); sub mtr_full_hostname ();
sub mtr_short_hostname (); sub mtr_short_hostname ();
sub mtr_init_args ($); sub mtr_init_args ($);
sub mtr_add_arg ($$); sub mtr_add_arg ($$@);
sub mtr_path_exists(@); sub mtr_path_exists(@);
sub mtr_script_exists(@); sub mtr_script_exists(@);
sub mtr_file_exists(@);
sub mtr_exe_exists(@); sub mtr_exe_exists(@);
sub mtr_copy_dir($$); sub mtr_copy_dir($$);
sub mtr_same_opts($$); sub mtr_same_opts($$);
...@@ -54,7 +55,7 @@ sub mtr_init_args ($) { ...@@ -54,7 +55,7 @@ sub mtr_init_args ($) {
$$args = []; # Empty list $$args = []; # Empty list
} }
sub mtr_add_arg ($$) { sub mtr_add_arg ($$@) {
my $args= shift; my $args= shift;
my $format= shift; my $format= shift;
my @fargs = @_; my @fargs = @_;
...@@ -101,6 +102,14 @@ sub mtr_script_exists (@) { ...@@ -101,6 +102,14 @@ sub mtr_script_exists (@) {
} }
} }
sub mtr_file_exists (@) {
foreach my $path ( @_ )
{
return $path if -e $path;
}
return "";
}
sub mtr_exe_exists (@) { sub mtr_exe_exists (@) {
my @path= @_; my @path= @_;
map {$_.= ".exe"} @path if $::glob_win32; map {$_.= ".exe"} @path if $::glob_win32;
...@@ -125,19 +134,30 @@ sub mtr_exe_exists (@) { ...@@ -125,19 +134,30 @@ sub mtr_exe_exists (@) {
} }
} }
sub mtr_copy_dir($$) { sub mtr_copy_dir($$) {
my $srcdir= shift; my $from_dir= shift;
my $dstdir= shift; my $to_dir= shift;
# Create destination directory # mtr_verbose("Copying from $from_dir to $to_dir");
mkpath($dstdir);
find(\&mtr_copy_one_file, $dstdir); mkpath("$to_dir");
} opendir(DIR, "$from_dir")
or mtr_error("Can't find $from_dir$!");
for(readdir(DIR)) {
next if "$_" eq "." or "$_" eq "..";
if ( -d "$from_dir/$_" )
{
mtr_copy_dir("$from_dir/$_", "$to_dir/$_");
next;
}
copy("$from_dir/$_", "$to_dir/$_");
}
closedir(DIR);
sub mtr_copy_one_file {
print $File::Find::name, "\n";
} }
sub mtr_same_opts ($$) { sub mtr_same_opts ($$) {
my $l1= shift; my $l1= shift;
my $l2= shift; my $l2= shift;
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
# and is part of the translation of the Bourne shell script with the # and is part of the translation of the Bourne shell script with the
# same name. # same name.
#use Carp qw(cluck);
use Socket; use Socket;
use Errno; use Errno;
use strict; use strict;
...@@ -14,12 +13,17 @@ use POSIX 'WNOHANG'; ...@@ -14,12 +13,17 @@ use POSIX 'WNOHANG';
sub mtr_run ($$$$$$;$); sub mtr_run ($$$$$$;$);
sub mtr_spawn ($$$$$$;$); sub mtr_spawn ($$$$$$;$);
sub mtr_stop_mysqld_servers ($); sub mtr_check_stop_servers ($);
sub mtr_kill_leftovers (); sub mtr_kill_leftovers ();
sub mtr_wait_blocking ($);
sub mtr_record_dead_children (); sub mtr_record_dead_children ();
sub mtr_ndbmgm_start($$);
sub mtr_mysqladmin_start($$$);
sub mtr_exit ($); sub mtr_exit ($);
sub sleep_until_file_created ($$$); sub sleep_until_file_created ($$$);
sub mtr_kill_processes ($); sub mtr_kill_processes ($);
sub mtr_ping_with_timeout($);
sub mtr_ping_port ($);
# static in C # static in C
sub spawn_impl ($$$$$$$$); sub spawn_impl ($$$$$$$$);
...@@ -31,7 +35,6 @@ sub spawn_impl ($$$$$$$$); ...@@ -31,7 +35,6 @@ sub spawn_impl ($$$$$$$$);
############################################################################## ##############################################################################
# This function try to mimic the C version used in "netware/mysql_test_run.c" # This function try to mimic the C version used in "netware/mysql_test_run.c"
# FIXME learn it to handle append mode as well, a "new" flag or a "append"
sub mtr_run ($$$$$$;$) { sub mtr_run ($$$$$$;$) {
my $path= shift; my $path= shift;
...@@ -112,6 +115,9 @@ sub spawn_impl ($$$$$$$$) { ...@@ -112,6 +115,9 @@ sub spawn_impl ($$$$$$$$) {
print STDERR "#### ", "-" x 78, "\n"; print STDERR "#### ", "-" x 78, "\n";
} }
mtr_error("Can't spawn with empty \"path\"") unless defined $path;
FORK: FORK:
{ {
my $pid= fork(); my $pid= fork();
...@@ -144,17 +150,6 @@ sub spawn_impl ($$$$$$$$) { ...@@ -144,17 +150,6 @@ sub spawn_impl ($$$$$$$$) {
$SIG{INT}= 'DEFAULT'; # Parent do some stuff, we don't $SIG{INT}= 'DEFAULT'; # Parent do some stuff, we don't
if ( $::glob_cygwin_shell and $mode eq 'test' )
{
# Programs started from mysqltest under Cygwin, are to
# execute them within Cygwin. Else simple things in test
# files like
# --system "echo 1 > file"
# will fail.
# FIXME not working :-(
# $ENV{'COMSPEC'}= "$::glob_cygwin_shell -c";
}
my $log_file_open_mode = '>'; my $log_file_open_mode = '>';
if ($spawn_opts and $spawn_opts->{'append_log_file'}) if ($spawn_opts and $spawn_opts->{'append_log_file'})
...@@ -164,7 +159,15 @@ sub spawn_impl ($$$$$$$$) { ...@@ -164,7 +159,15 @@ sub spawn_impl ($$$$$$$$) {
if ( $output ) if ( $output )
{ {
if ( ! open(STDOUT,$log_file_open_mode,$output) ) if ( $::glob_win32_perl )
{
# Don't redirect stdout on ActiveState perl since this is
# just another thread in the same process.
# Should be fixed so that the thread that is created with fork
# executes the exe in another process and wait's for it to return.
# In the meanwhile, we get all the output from mysqld's to screen
}
elsif ( ! open(STDOUT,$log_file_open_mode,$output) )
{ {
mtr_child_error("can't redirect STDOUT to \"$output\": $!"); mtr_child_error("can't redirect STDOUT to \"$output\": $!");
} }
...@@ -216,8 +219,7 @@ sub spawn_parent_impl { ...@@ -216,8 +219,7 @@ sub spawn_parent_impl {
{ {
# Simple run of command, we wait for it to return # Simple run of command, we wait for it to return
my $ret_pid= waitpid($pid,0); my $ret_pid= waitpid($pid,0);
if ( $ret_pid != $pid )
if ( $ret_pid <= 0 )
{ {
mtr_error("$path ($pid) got lost somehow"); mtr_error("$path ($pid) got lost somehow");
} }
...@@ -245,7 +247,6 @@ sub spawn_parent_impl { ...@@ -245,7 +247,6 @@ sub spawn_parent_impl {
# Someone terminated, don't know who. Collect # Someone terminated, don't know who. Collect
# status info first before $? is lost, # status info first before $? is lost,
# but not $exit_value, this is flagged from # but not $exit_value, this is flagged from
#
my $timer_name= mtr_timer_timeout($::glob_timers, $ret_pid); my $timer_name= mtr_timer_timeout($::glob_timers, $ret_pid);
if ( $timer_name ) if ( $timer_name )
...@@ -272,45 +273,22 @@ sub spawn_parent_impl { ...@@ -272,45 +273,22 @@ sub spawn_parent_impl {
last; last;
} }
# If one of the mysqld processes died, we want to # One of the child processes died, unless this was expected
# mark this, and kill the mysqltest process. # mysqltest should be killed and test aborted
foreach my $idx (0..1) check_expected_crash_and_restart($ret_pid);
{
if ( $::master->[$idx]->{'pid'} eq $ret_pid )
{
mtr_debug("child $ret_pid was master[$idx], " .
"exit during mysqltest run");
$::master->[$idx]->{'pid'}= 0;
last;
}
}
foreach my $idx (0..2)
{
if ( $::slave->[$idx]->{'pid'} eq $ret_pid )
{
mtr_debug("child $ret_pid was slave[$idx], " .
"exit during mysqltest run");
$::slave->[$idx]->{'pid'}= 0;
last;
}
}
mtr_debug("waitpid() catched exit of unknown child $ret_pid, " .
"exit during mysqltest run");
} }
if ( $ret_pid != $pid ) if ( $ret_pid != $pid )
{ {
# We terminated the waiting because a "mysqld" process died. # We terminated the waiting because a "mysqld" process died.
# Kill the mysqltest process. # Kill the mysqltest process.
mtr_verbose("Kill mysqltest because another process died");
kill(9,$pid); kill(9,$pid);
$ret_pid= waitpid($pid,0); $ret_pid= waitpid($pid,0);
if ( $ret_pid == -1 ) if ( $ret_pid != $pid )
{ {
mtr_error("$path ($pid) got lost somehow"); mtr_error("$path ($pid) got lost somehow");
} }
...@@ -351,39 +329,101 @@ sub mtr_process_exit_status { ...@@ -351,39 +329,101 @@ sub mtr_process_exit_status {
# #
############################################################################## ##############################################################################
# We just "ping" on the ports, and if we can't do a socket connect
# we assume the server is dead. So we don't *really* know a server
# is dead, we just hope that it after letting the listen port go,
# it is dead enough for us to start a new server.
# Kill all processes(mysqld, ndbd, ndb_mgmd and im) that would conflict with
# this run
# Make sure to remove the PID file, if any.
# kill IM manager first, else it will restart the servers
sub mtr_kill_leftovers () { sub mtr_kill_leftovers () {
# First, kill all masters and slaves that would conflict with mtr_report("Killing Possible Leftover Processes");
# this run. Make sure to remove the PID file, if any. mtr_debug("mtr_kill_leftovers(): started.");
mkpath("$::opt_vardir/log"); # Needed for mysqladmin log
# Stop or kill Instance Manager and all its children. If we failed to do
# that, we can only abort -- there is nothing left to do.
# mtr_error("Failed to stop Instance Manager.")
# unless mtr_im_stop($::instance_manager);
# Start shutdown of masters and slaves. Don't touch IM-managed mysqld
# instances -- they should be stopped by mtr_im_stop().
mtr_debug("Shutting down mysqld-instances...");
my @args; my @kill_pids;
my %admin_pids;
for ( my $idx; $idx < 2; $idx++ ) foreach my $srv (@{$::master}, @{$::slave})
{ {
push(@args,{ mtr_debug(" - mysqld " .
pid => 0, # We don't know the PID "(pid: $srv->{pid}; " .
pidfile => $::master->[$idx]->{'path_mypid'}, "pid file: '$srv->{path_pid}'; " .
sockfile => $::master->[$idx]->{'path_mysock'}, "socket: '$srv->{path_sock}'; ".
port => $::master->[$idx]->{'path_myport'}, "port: $srv->{port})");
my $pid= mtr_mysqladmin_start($srv, "shutdown", 70);
# Save the pid of the mysqladmin process
$admin_pids{$pid}= 1;
push(@kill_pids,{
pid => $srv->{'pid'},
pidfile => $srv->{'path_pid'},
sockfile => $srv->{'path_sock'},
port => $srv->{'port'},
}); });
$srv->{'pid'}= 0; # Assume we are done with it
} }
for ( my $idx; $idx < 3; $idx++ ) if ( ! $::opt_skip_ndbcluster )
{
# Start shutdown of clusters.
mtr_debug("Shutting down cluster...");
foreach my $cluster (@{$::clusters})
{
mtr_debug(" - cluster " .
"(pid: $cluster->{pid}; " .
"pid file: '$cluster->{path_pid})");
my $pid= mtr_ndbmgm_start($cluster, "shutdown");
# Save the pid of the ndb_mgm process
$admin_pids{$pid}= 1;
push(@kill_pids,{
pid => $cluster->{'pid'},
pidfile => $cluster->{'path_pid'}
});
$cluster->{'pid'}= 0; # Assume we are done with it
foreach my $ndbd (@{$cluster->{'ndbds'}})
{ {
push(@args,{ mtr_debug(" - ndbd " .
pid => 0, # We don't know the PID "(pid: $ndbd->{pid}; " .
pidfile => $::slave->[$idx]->{'path_mypid'}, "pid file: '$ndbd->{path_pid})");
sockfile => $::slave->[$idx]->{'path_mysock'},
port => $::slave->[$idx]->{'path_myport'}, push(@kill_pids,{
pid => $ndbd->{'pid'},
pidfile => $ndbd->{'path_pid'},
}); });
$ndbd->{'pid'}= 0; # Assume we are done with it
}
} }
}
# Wait for all the admin processes to complete
mtr_wait_blocking(\%admin_pids);
# If we trusted "mysqladmin --shutdown_timeout= ..." we could just
# terminate now, but we don't (FIXME should be debugged).
# So we try again to ping and at least wait the same amount of time
# mysqladmin would for all to die.
mtr_mysqladmin_shutdown(\@args, 20); mtr_ping_with_timeout(\@kill_pids);
# We now have tried to terminate nice. We have waited for the listen # We now have tried to terminate nice. We have waited for the listen
# port to be free, but can't really tell if the mysqld process died # port to be free, but can't really tell if the mysqld process died
...@@ -401,6 +441,8 @@ sub mtr_kill_leftovers () { ...@@ -401,6 +441,8 @@ sub mtr_kill_leftovers () {
# FIXME $path_run_dir or something # FIXME $path_run_dir or something
my $rundir= "$::opt_vardir/run"; my $rundir= "$::opt_vardir/run";
mtr_debug("Processing PID files in directory '$rundir'...");
if ( -d $rundir ) if ( -d $rundir )
{ {
opendir(RUNDIR, $rundir) opendir(RUNDIR, $rundir)
...@@ -414,26 +456,32 @@ sub mtr_kill_leftovers () { ...@@ -414,26 +456,32 @@ sub mtr_kill_leftovers () {
if ( -f $pidfile ) if ( -f $pidfile )
{ {
my $pid= mtr_get_pid_from_file($pidfile); mtr_debug("Processing PID file: '$pidfile'...");
# Race, could have been removed between I tested with -f my $pid= mtr_get_pid_from_file($pidfile);
# and the unlink() below, so I better check again with -f
if ( ! unlink($pidfile) and -f $pidfile ) mtr_debug("Got pid: $pid from file '$pidfile'");
{
mtr_error("can't remove $pidfile");
}
if ( $::glob_cygwin_perl or kill(0, $pid) ) if ( $::glob_cygwin_perl or kill(0, $pid) )
{ {
mtr_debug("There is process with pid $pid -- scheduling for kill.");
push(@pids, $pid); # We know (cygwin guess) it exists push(@pids, $pid); # We know (cygwin guess) it exists
} }
else
{
mtr_debug("There is no process with pid $pid -- skipping.");
}
} }
} }
closedir(RUNDIR); closedir(RUNDIR);
if ( @pids ) if ( @pids )
{ {
mtr_debug("Killing the following processes with PID files: " .
join(' ', @pids) . "...");
start_reap_all();
if ( $::glob_cygwin_perl ) if ( $::glob_cygwin_perl )
{ {
# We have no (easy) way of knowing the Cygwin controlling # We have no (easy) way of knowing the Cygwin controlling
...@@ -447,8 +495,9 @@ sub mtr_kill_leftovers () { ...@@ -447,8 +495,9 @@ sub mtr_kill_leftovers () {
my $retries= 10; # 10 seconds my $retries= 10; # 10 seconds
do do
{ {
mtr_debug("Sending SIGKILL to pids: " . join(' ', @pids));
kill(9, @pids); kill(9, @pids);
mtr_debug("Sleep 1 second waiting for processes to die"); mtr_report("Sleep 1 second waiting for processes to die");
sleep(1) # Wait one second sleep(1) # Wait one second
} while ( $retries-- and kill(0, @pids) ); } while ( $retries-- and kill(0, @pids) );
...@@ -457,57 +506,75 @@ sub mtr_kill_leftovers () { ...@@ -457,57 +506,75 @@ sub mtr_kill_leftovers () {
mtr_warning("can't kill process(es) " . join(" ", @pids)); mtr_warning("can't kill process(es) " . join(" ", @pids));
} }
} }
stop_reap_all();
}
} }
else
{
mtr_debug("Directory for PID files ($rundir) does not exist.");
} }
# We may have failed everything, bug we now check again if we have # We may have failed everything, but we now check again if we have
# the listen ports free to use, and if they are free, just go for it. # the listen ports free to use, and if they are free, just go for it.
foreach my $srv ( @args ) mtr_debug("Checking known mysqld servers...");
foreach my $srv ( @kill_pids )
{ {
if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) if ( defined $srv->{'port'} and mtr_ping_port($srv->{'port'}) )
{ {
mtr_warning("can't kill old mysqld holding port $srv->{'port'}"); mtr_warning("can't kill old process holding port $srv->{'port'}");
} }
} }
}
############################################################################## mtr_debug("mtr_kill_leftovers(): finished.");
# }
# Shut down mysqld servers we have started from this run of this script
#
##############################################################################
# To speed things we kill servers in parallel. The argument is a list
# of 'ports', 'pids', 'pidfiles' and 'socketfiles'.
# Check that all processes in list are killed
# The argument is a list of 'ports', 'pids', 'pidfiles' and 'socketfiles'
# for which shutdown has been started. Make sure they all get killed
# in one way or the other.
#
# FIXME On Cygwin, and maybe some other platforms, $srv->{'pid'} and # FIXME On Cygwin, and maybe some other platforms, $srv->{'pid'} and
# $srv->{'pidfile'} will not be the same PID. We need to try to kill # the pid in $srv->{'pidfile'} will not be the same PID. We need to try to kill
# both I think. # both I think.
sub mtr_stop_mysqld_servers ($) { sub mtr_check_stop_servers ($) {
my $spec= shift; my $spec= shift;
# ---------------------------------------------------------------------- # Return if no processes are defined
# First try nice normal shutdown using 'mysqladmin' return if ! @$spec;
# ----------------------------------------------------------------------
# Shutdown time must be high as slave may be in reconnect #mtr_report("mtr_check_stop_servers");
mtr_mysqladmin_shutdown($spec, 70);
mtr_ping_with_timeout(\@$spec);
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# We loop with waitpid() nonblocking to see how many of the ones we # We loop with waitpid() nonblocking to see how many of the ones we
# are to kill, actually got killed by mtr_mysqladmin_shutdown(). # are to kill, actually got killed by mysqladmin or ndb_mgm
# Note that we don't rely on this, the mysqld server might have stop #
# Note that we don't rely on this, the mysqld server might have stopped
# listening to the port, but still be alive. But it is a start. # listening to the port, but still be alive. But it is a start.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
foreach my $srv ( @$spec ) foreach my $srv ( @$spec )
{ {
if ( $srv->{'pid'} and (waitpid($srv->{'pid'},&WNOHANG) == $srv->{'pid'}) ) my $ret_pid;
if ( $srv->{'pid'} )
{
$ret_pid= waitpid($srv->{'pid'},&WNOHANG);
if ($ret_pid == $srv->{'pid'})
{ {
mtr_verbose("Caught exit of process $ret_pid");
$srv->{'pid'}= 0; $srv->{'pid'}= 0;
} }
else
{
# mtr_warning("caught exit of unknown child $ret_pid");
}
}
} }
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
...@@ -540,13 +607,12 @@ sub mtr_stop_mysqld_servers ($) { ...@@ -540,13 +607,12 @@ sub mtr_stop_mysqld_servers ($) {
} }
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# If the processes where started from this script, and we had no PIDS # If all the processes in list already have been killed,
# then we don't have to do anything. # then we don't have to do anything.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
if ( ! keys %mysqld_pids ) if ( ! keys %mysqld_pids )
{ {
# cluck "This is how we got here!";
return; return;
} }
...@@ -595,20 +661,30 @@ sub mtr_stop_mysqld_servers ($) { ...@@ -595,20 +661,30 @@ sub mtr_stop_mysqld_servers ($) {
foreach my $file ($srv->{'pidfile'}, $srv->{'sockfile'}) foreach my $file ($srv->{'pidfile'}, $srv->{'sockfile'})
{ {
# Know it is dead so should be no race, careful anyway # Know it is dead so should be no race, careful anyway
if ( -f $file and ! unlink($file) and -f $file ) if ( defined $file and -f $file and ! unlink($file) and -f $file )
{ {
$errors++; $errors++;
mtr_warning("couldn't delete $file"); mtr_warning("couldn't delete $file");
} }
} }
$srv->{'pid'}= 0;
} }
} }
} }
if ( $errors ) if ( $errors )
{ {
# We are in trouble, just die.... # There where errors killing processes
# do one last attempt to ping the servers
# and if they can't be pinged, assume they are dead
if ( ! mtr_ping_with_timeout( \@$spec ) )
{
mtr_error("we could not kill or clean up all processes"); mtr_error("we could not kill or clean up all processes");
} }
else
{
mtr_verbose("All ports were free, continuing");
}
}
} }
# FIXME We just assume they are all dead, for Cygwin we are not # FIXME We just assume they are all dead, for Cygwin we are not
...@@ -616,156 +692,281 @@ sub mtr_stop_mysqld_servers ($) { ...@@ -616,156 +692,281 @@ sub mtr_stop_mysqld_servers ($) {
} }
# Wait for all the process in the list to terminate
sub mtr_wait_blocking($) {
my $admin_pids= shift;
##############################################################################
#
# Shut down mysqld servers using "mysqladmin ... shutdown".
# To speed this up, we start them in parallel and use waitpid() to
# catch their termination. Note that this doesn't say the servers
# are terminated, just that 'mysqladmin' is terminated.
#
# Note that mysqladmin will ask the server about what PID file it uses,
# and mysqladmin will wait for it to be removed before it terminates
# (unless passes timeout).
#
# This function will take at most about 20 seconds, and we still are not
# sure we killed them all. If none is responding to ping, we return 1,
# else we return 0.
#
##############################################################################
sub mtr_mysqladmin_shutdown { # Return if no processes defined
my $spec= shift; return if ! %$admin_pids;
my $adm_shutdown_tmo= shift;
my %mysql_admin_pids; mtr_verbose("mtr_wait_blocking");
my @to_kill_specs;
foreach my $srv ( @$spec ) # Wait for all the started processes to exit
# As mysqladmin is such a simple program, we trust it to terminate itself.
# I.e. we wait blocking, and wait for them all before we go on.
foreach my $pid (keys %{$admin_pids})
{ {
if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) my $ret_pid= waitpid($pid,0);
{
push(@to_kill_specs, $srv);
}
} }
}
# Start "mysqladmin shutdown" for a specific mysqld
sub mtr_mysqladmin_start($$$) {
my $srv= shift;
my $command= shift;
my $adm_shutdown_tmo= shift;
foreach my $srv ( @to_kill_specs )
{
# FIXME wrong log.....
# FIXME, stderr.....
# Shutdown time must be high as slave may be in reconnect
my $args; my $args;
mtr_init_args(\$args); mtr_init_args(\$args);
mtr_add_arg($args, "--no-defaults"); mtr_add_arg($args, "--no-defaults");
mtr_add_arg($args, "--user=%s", $::opt_user); mtr_add_arg($args, "--user=%s", $::opt_user);
mtr_add_arg($args, "--password="); mtr_add_arg($args, "--password=");
if ( -e $srv->{'sockfile'} ) mtr_add_arg($args, "--silent");
if ( -e $srv->{'path_sock'} )
{ {
mtr_add_arg($args, "--socket=%s", $srv->{'sockfile'}); mtr_add_arg($args, "--socket=%s", $srv->{'path_sock'});
} }
if ( $srv->{'port'} ) if ( $srv->{'port'} )
{ {
mtr_add_arg($args, "--port=%s", $srv->{'port'}); mtr_add_arg($args, "--port=%s", $srv->{'port'});
} }
if ( $srv->{'port'} and ! -e $srv->{'sockfile'} ) if ( $srv->{'port'} and ! -e $srv->{'path_sock'} )
{ {
mtr_add_arg($args, "--protocol=tcp"); # Needed if no --socket mtr_add_arg($args, "--protocol=tcp"); # Needed if no --socket
} }
mtr_add_arg($args, "--connect_timeout=5"); mtr_add_arg($args, "--connect_timeout=5");
# Shutdown time must be high as slave may be in reconnect
mtr_add_arg($args, "--shutdown_timeout=$adm_shutdown_tmo"); mtr_add_arg($args, "--shutdown_timeout=$adm_shutdown_tmo");
mtr_add_arg($args, "shutdown"); mtr_add_arg($args, "$command");
# We don't wait for termination of mysqladmin my $path_mysqladmin_log= "$::opt_vardir/log/mysqladmin.log";
my $pid= mtr_spawn($::exe_mysqladmin, $args, my $pid= mtr_spawn($::exe_mysqladmin, $args,
"", $::path_manager_log, $::path_manager_log, "", "", $path_mysqladmin_log, $path_mysqladmin_log, "",
{ append_log_file => 1 }); { append_log_file => 1 });
$mysql_admin_pids{$pid}= 1; mtr_verbose("mtr_mysqladmin_start, pid: $pid");
} return $pid;
# As mysqladmin is such a simple program, we trust it to terminate. }
# I.e. we wait blocking, and wait wait for them all before we go on.
while (keys %mysql_admin_pids)
{
foreach my $pid (keys %mysql_admin_pids)
{
if ( waitpid($pid,0) > 0 )
{
delete $mysql_admin_pids{$pid};
}
}
}
# If we trusted "mysqladmin --shutdown_timeout= ..." we could just # Start "ndb_mgm shutdown" for a specific cluster, it will
# terminate now, but we don't (FIXME should be debugged). # shutdown all data nodes and leave the ndb_mgmd running
# So we try again to ping and at least wait the same amount of time sub mtr_ndbmgm_start($$) {
# mysqladmin would for all to die. my $cluster= shift;
my $command= shift;
my $args;
my $timeout= 20; # 20 seconds max mtr_init_args(\$args);
mtr_add_arg($args, "--no-defaults");
mtr_add_arg($args, "--core");
mtr_add_arg($args, "--try-reconnect=1");
mtr_add_arg($args, "--ndb_connectstring=%s", $cluster->{'connect_string'});
mtr_add_arg($args, "-e");
mtr_add_arg($args, "$command");
my $pid= mtr_spawn($::exe_ndb_mgm, $args,
"", "/dev/null", "/dev/null", "",
{});
mtr_verbose("mtr_ndbmgm_start, pid: $pid");
return $pid;
}
# Ping all servers in list, exit when none of them answers
# or when timeout has passed
sub mtr_ping_with_timeout($) {
my $spec= shift;
my $timeout= 200; # 20 seconds max
my $res= 1; # If we just fall through, we are done my $res= 1; # If we just fall through, we are done
# in the sense that the servers don't # in the sense that the servers don't
# listen to their ports any longer # listen to their ports any longer
mtr_debug("Waiting for mysqld servers to stop...");
TIME: TIME:
while ( $timeout-- ) while ( $timeout-- )
{ {
foreach my $srv ( @to_kill_specs ) foreach my $srv ( @$spec )
{ {
$res= 1; # We are optimistic $res= 1; # We are optimistic
if ( mtr_ping_mysqld_server($srv->{'port'}, $srv->{'sockfile'}) ) if ( $srv->{'pid'} and defined $srv->{'port'} )
{
if ( mtr_ping_port($srv->{'port'}) )
{ {
mtr_debug("Sleep 1 second waiting for processes to stop using port"); mtr_verbose("waiting for process $srv->{'pid'} to stop ".
sleep(1); # One second "using port $srv->{'port'}");
# Millisceond sleep emulated with select
select(undef, undef, undef, (0.1));
$res= 0; $res= 0;
next TIME; next TIME;
} }
else
{
# Process was not using port
}
}
} }
last; # If we got here, we are done last; # If we got here, we are done
} }
$timeout or mtr_debug("At least one server is still listening to its port"); if ($res)
{
sleep(5) if $::glob_win32; # FIXME next startup fails if no sleep mtr_debug("mtr_ping_with_timeout(): All mysqld instances are down.");
}
else
{
mtr_report("mtr_ping_with_timeout(): At least one server is alive.");
}
return $res; return $res;
} }
##############################################################################
# #
# The operating system will keep information about dead children, # Loop through our list of processes and look for and entry
# we read this information here, and if we have records the process # with the provided pid
# is alive, we mark it as dead. # Set the pid of that process to 0 if found
# #
############################################################################## sub mark_process_dead($)
{
my $ret_pid= shift;
sub mtr_record_dead_children () { foreach my $mysqld (@{$::master}, @{$::slave})
{
if ( $mysqld->{'pid'} eq $ret_pid )
{
mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid");
$mysqld->{'pid'}= 0;
return;
}
}
my $ret_pid; foreach my $cluster (@{$::clusters})
{
if ( $cluster->{'pid'} eq $ret_pid )
{
mtr_verbose("$cluster->{'name'} cluster ndb_mgmd exited, pid: $ret_pid");
$cluster->{'pid'}= 0;
return;
}
foreach my $ndbd (@{$cluster->{'ndbds'}})
{
if ( $ndbd->{'pid'} eq $ret_pid )
{
mtr_verbose("$cluster->{'name'} cluster ndbd exited, pid: $ret_pid");
$ndbd->{'pid'}= 0;
return;
}
}
}
mtr_warning("mark_process_dead couldn't find an entry for pid: $ret_pid");
}
#
# Loop through our list of processes and look for and entry
# with the provided pid, if found check for the file indicating
# expected crash and restart it.
#
sub check_expected_crash_and_restart($)
{
my $ret_pid= shift;
foreach my $mysqld (@{$::master}, @{$::slave})
{
if ( $mysqld->{'pid'} eq $ret_pid )
{
mtr_verbose("$mysqld->{'type'} $mysqld->{'idx'} exited, pid: $ret_pid");
$mysqld->{'pid'}= 0;
# FIXME the man page says to wait for -1 to terminate, # Check if crash expected and restart if it was
# but on OS X we get '0' all the time... my $expect_file= "$::opt_vardir/tmp/" . "$mysqld->{'type'}" .
while ( ($ret_pid= waitpid(-1,&WNOHANG)) > 0 ) "$mysqld->{'idx'}" . ".expect";
if ( -f $expect_file )
{ {
mtr_debug("waitpid() catched exit of child $ret_pid"); mtr_verbose("Crash was expected, file $expect_file exists");
foreach my $idx (0..1) mysqld_start($mysqld, $mysqld->{'start_opts'},
$mysqld->{'start_slave_master_info'});
unlink($expect_file);
}
return;
}
}
foreach my $cluster (@{$::clusters})
{
if ( $cluster->{'pid'} eq $ret_pid )
{ {
if ( $::master->[$idx]->{'pid'} eq $ret_pid ) mtr_verbose("$cluster->{'name'} cluster ndb_mgmd exited, pid: $ret_pid");
$cluster->{'pid'}= 0;
# Check if crash expected and restart if it was
my $expect_file= "$::opt_vardir/tmp/ndb_mgmd_" . "$cluster->{'type'}" .
".expect";
if ( -f $expect_file )
{ {
mtr_debug("child $ret_pid was master[$idx]"); mtr_verbose("Crash was expected, file $expect_file exists");
$::master->[$idx]->{'pid'}= 0; ndbmgmd_start($cluster);
unlink($expect_file);
} }
return;
} }
foreach my $idx (0..2) foreach my $ndbd (@{$cluster->{'ndbds'}})
{ {
if ( $::slave->[$idx]->{'pid'} eq $ret_pid ) if ( $ndbd->{'pid'} eq $ret_pid )
{ {
mtr_debug("child $ret_pid was slave[$idx]"); mtr_verbose("$cluster->{'name'} cluster ndbd exited, pid: $ret_pid");
$::slave->[$idx]->{'pid'}= 0; $ndbd->{'pid'}= 0;
last;
# Check if crash expected and restart if it was
my $expect_file= "$::opt_vardir/tmp/ndbd_" . "$cluster->{'type'}" .
"$ndbd->{'idx'}" . ".expect";
if ( -f $expect_file )
{
mtr_verbose("Crash was expected, file $expect_file exists");
ndbd_start($cluster, $ndbd->{'idx'},
$ndbd->{'start_extra_args'});
unlink($expect_file);
}
return;
} }
} }
} }
mtr_warning("check_expected_crash_and_restart couldn't find an entry for pid: $ret_pid");
}
##############################################################################
#
# The operating system will keep information about dead children,
# we read this information here, and if we have records the process
# is alive, we mark it as dead.
#
##############################################################################
sub mtr_record_dead_children () {
my $process_died= 0;
my $ret_pid;
# Wait without blockinng to see if any processes had died
# -1 or 0 means there are no more procesess to wait for
while ( ($ret_pid= waitpid(-1,&WNOHANG)) != 0 and $ret_pid != -1)
{
mtr_warning("mtr_record_dead_children: $ret_pid");
mark_process_dead($ret_pid);
$process_died= 1;
}
return $process_died;
} }
sub start_reap_all { sub start_reap_all {
...@@ -777,16 +978,24 @@ sub start_reap_all { ...@@ -777,16 +978,24 @@ sub start_reap_all {
# here. If a process terminated before setting $SIG{CHLD} (but after # here. If a process terminated before setting $SIG{CHLD} (but after
# any attempt to waitpid() it), it will still be a zombie. So we # any attempt to waitpid() it), it will still be a zombie. So we
# have to handle any such process here. # have to handle any such process here.
while(waitpid(-1, &WNOHANG) > 0) { }; my $pid;
while(($pid= waitpid(-1, &WNOHANG)) != 0 and $pid != -1)
{
mtr_warning("start_reap_all pid: $pid");
mark_process_dead($pid);
};
} }
sub stop_reap_all { sub stop_reap_all {
$SIG{CHLD}= 'DEFAULT'; $SIG{CHLD}= 'DEFAULT';
} }
sub mtr_ping_mysqld_server () {
sub mtr_ping_port ($) {
my $port= shift; my $port= shift;
mtr_verbose("mtr_ping_port: $port");
my $remote= "localhost"; my $remote= "localhost";
my $iaddr= inet_aton($remote); my $iaddr= inet_aton($remote);
if ( ! $iaddr ) if ( ! $iaddr )
...@@ -799,13 +1008,18 @@ sub mtr_ping_mysqld_server () { ...@@ -799,13 +1008,18 @@ sub mtr_ping_mysqld_server () {
{ {
mtr_error("can't create socket: $!"); mtr_error("can't create socket: $!");
} }
mtr_debug("Pinging server (port: $port)...");
if ( connect(SOCK, $paddr) ) if ( connect(SOCK, $paddr) )
{ {
close(SOCK); # FIXME check error? close(SOCK); # FIXME check error?
mtr_verbose("USED");
return 1; return 1;
} }
else else
{ {
mtr_verbose("FREE");
return 0; return 0;
} }
} }
...@@ -822,8 +1036,10 @@ sub sleep_until_file_created ($$$) { ...@@ -822,8 +1036,10 @@ sub sleep_until_file_created ($$$) {
my $pidfile= shift; my $pidfile= shift;
my $timeout= shift; my $timeout= shift;
my $pid= shift; my $pid= shift;
my $sleeptime= 100; # Milliseconds
my $loops= ($timeout * 1000) / $sleeptime;
for ( my $loop= 1; $loop <= $timeout; $loop++ ) for ( my $loop= 1; $loop <= $loops; $loop++ )
{ {
if ( -r $pidfile ) if ( -r $pidfile )
{ {
...@@ -831,21 +1047,25 @@ sub sleep_until_file_created ($$$) { ...@@ -831,21 +1047,25 @@ sub sleep_until_file_created ($$$) {
} }
# Check if it died after the fork() was successful # Check if it died after the fork() was successful
if ( waitpid($pid,&WNOHANG) == $pid ) if ( $pid != 0 && waitpid($pid,&WNOHANG) == $pid )
{ {
mtr_warning("Process $pid died");
return 0; return 0;
} }
mtr_debug("Sleep 1 second waiting for creation of $pidfile"); mtr_debug("Sleep $sleeptime milliseconds waiting for $pidfile");
if ( $loop % 60 == 0 ) # Print extra message every 60 seconds
my $seconds= ($loop * $sleeptime) / 1000;
if ( $seconds > 1 and int($seconds) % 60 == 0 )
{ {
my $left= $timeout - $loop; my $left= $timeout - $seconds;
mtr_warning("Waited $loop seconds for $pidfile to be created, " . mtr_warning("Waited $seconds seconds for $pidfile to be created, " .
"still waiting for $left seconds..."); "still waiting for $left seconds...");
} }
sleep(1); # Millisceond sleep emulated with select
select(undef, undef, undef, ($sleeptime/1000));
} }
return 0; return 0;
...@@ -855,18 +1075,18 @@ sub sleep_until_file_created ($$$) { ...@@ -855,18 +1075,18 @@ sub sleep_until_file_created ($$$) {
sub mtr_kill_processes ($) { sub mtr_kill_processes ($) {
my $pids = shift; my $pids = shift;
foreach my $sig (15,9) mtr_verbose("mtr_kill_processes " . join(" ", @$pids));
foreach my $pid (@$pids)
{ {
my $retries= 20; # FIXME 20 seconds, this is silly! foreach my $sig (15, 9)
kill($sig, @{$pids});
while ( $retries-- and kill(0, @{$pids}) )
{ {
mtr_debug("Sleep 1 second waiting for processes to die"); last if mtr_im_kill_process([ $pid ], $sig, 10, 1);
sleep(1) # Wait one second
} }
} }
} }
############################################################################## ##############################################################################
# #
# When we exit, we kill off all children # When we exit, we kill off all children
...@@ -876,7 +1096,7 @@ sub mtr_kill_processes ($) { ...@@ -876,7 +1096,7 @@ sub mtr_kill_processes ($) {
# FIXME something is wrong, we sometimes terminate with "Hangup" written # FIXME something is wrong, we sometimes terminate with "Hangup" written
# to tty, and no STDERR output telling us why. # to tty, and no STDERR output telling us why.
# FIXME for some readon, setting HUP to 'IGNORE' will cause exit() to # FIXME for some reason, setting HUP to 'IGNORE' will cause exit() to
# write out "Hangup", and maybe loose some output. We insert a sleep... # write out "Hangup", and maybe loose some output. We insert a sleep...
sub mtr_exit ($) { sub mtr_exit ($) {
...@@ -884,9 +1104,18 @@ sub mtr_exit ($) { ...@@ -884,9 +1104,18 @@ sub mtr_exit ($) {
# cluck("Called mtr_exit()"); # cluck("Called mtr_exit()");
mtr_timer_stop_all($::glob_timers); mtr_timer_stop_all($::glob_timers);
local $SIG{HUP} = 'IGNORE'; local $SIG{HUP} = 'IGNORE';
kill('HUP', -$$); # ToDo: Signalling -$$ will only work if we are the process group
sleep 2; # leader (in fact on QNX it will signal our session group leader,
# which might be Do-compile or Pushbuild, causing tests to be
# aborted). So we only do it if we are the group leader. We might
# set ourselves as the group leader at startup (with
# POSIX::setpgrp(0,0)), but then care must be needed to always do
# proper child process cleanup.
kill('HUP', -$$) if !$::glob_win32_perl and $$ == getpgrp();
exit($code); exit($code);
} }
###########################################################################
1; 1;
...@@ -10,6 +10,7 @@ sub mtr_report_test_name($); ...@@ -10,6 +10,7 @@ sub mtr_report_test_name($);
sub mtr_report_test_passed($); sub mtr_report_test_passed($);
sub mtr_report_test_failed($); sub mtr_report_test_failed($);
sub mtr_report_test_skipped($); sub mtr_report_test_skipped($);
sub mtr_report_test_not_skipped_though_disabled($);
sub mtr_show_failed_diff ($); sub mtr_show_failed_diff ($);
sub mtr_report_stats ($); sub mtr_report_stats ($);
...@@ -21,6 +22,7 @@ sub mtr_warning (@); ...@@ -21,6 +22,7 @@ sub mtr_warning (@);
sub mtr_error (@); sub mtr_error (@);
sub mtr_child_error (@); sub mtr_child_error (@);
sub mtr_debug (@); sub mtr_debug (@);
sub mtr_verbose (@);
############################################################################## ##############################################################################
...@@ -36,6 +38,7 @@ sub mtr_show_failed_diff ($) { ...@@ -36,6 +38,7 @@ sub mtr_show_failed_diff ($) {
my $reject_file= "r/$tname.reject"; my $reject_file= "r/$tname.reject";
my $result_file= "r/$tname.result"; my $result_file= "r/$tname.result";
my $log_file= "r/$tname.log";
my $eval_file= "r/$tname.eval"; my $eval_file= "r/$tname.eval";
if ( $::opt_suite ne "main" ) if ( $::opt_suite ne "main" )
...@@ -43,6 +46,7 @@ sub mtr_show_failed_diff ($) { ...@@ -43,6 +46,7 @@ sub mtr_show_failed_diff ($) {
$reject_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$reject_file"; $reject_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$reject_file";
$result_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$result_file"; $result_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$result_file";
$eval_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$eval_file"; $eval_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$eval_file";
$log_file= "$::glob_mysql_test_dir/suite/$::opt_suite/$log_file";
} }
if ( -f $eval_file ) if ( -f $eval_file )
...@@ -70,6 +74,12 @@ sub mtr_show_failed_diff ($) { ...@@ -70,6 +74,12 @@ sub mtr_show_failed_diff ($) {
print "http://www.mysql.com/doc/en/Reporting_mysqltest_bugs.html\n"; print "http://www.mysql.com/doc/en/Reporting_mysqltest_bugs.html\n";
print "to find the reason to this problem and how to report this.\n\n"; print "to find the reason to this problem and how to report this.\n\n";
} }
if ( -f $log_file )
{
print "Result from queries before failure can be found in $log_file\n";
# FIXME Maybe a tail -f -n 10 $log_file here
}
} }
sub mtr_report_test_name ($) { sub mtr_report_test_name ($) {
...@@ -88,7 +98,24 @@ sub mtr_report_test_skipped ($) { ...@@ -88,7 +98,24 @@ sub mtr_report_test_skipped ($) {
} }
else else
{ {
print "[ skipped ]\n"; print "[ skipped ] $tinfo->{'comment'}\n";
}
}
sub mtr_report_tests_not_skipped_though_disabled ($) {
my $tests= shift;
if ( $::opt_enable_disabled )
{
my @disabled_tests= grep {$_->{'dont_skip_though_disabled'}} @$tests;
if ( @disabled_tests )
{
print "\nTest(s) which will be run though they are marked as disabled:\n";
foreach my $tinfo ( sort {$a->{'name'} cmp $b->{'name'}} @disabled_tests )
{
printf " %-20s : %s\n", $tinfo->{'name'}, $tinfo->{'comment'};
}
}
} }
} }
...@@ -99,7 +126,7 @@ sub mtr_report_test_passed ($) { ...@@ -99,7 +126,7 @@ sub mtr_report_test_passed ($) {
if ( $::opt_timer and -f "$::opt_vardir/log/timer" ) if ( $::opt_timer and -f "$::opt_vardir/log/timer" )
{ {
$timer= mtr_fromfile("$::opt_vardir/log/timer"); $timer= mtr_fromfile("$::opt_vardir/log/timer");
$::glob_tot_real_time += $timer; $::glob_tot_real_time += ($timer/1000);
$timer= sprintf "%12s", $timer; $timer= sprintf "%12s", $timer;
} }
$tinfo->{'result'}= 'MTR_RES_PASSED'; $tinfo->{'result'}= 'MTR_RES_PASSED';
...@@ -114,6 +141,11 @@ sub mtr_report_test_failed ($) { ...@@ -114,6 +141,11 @@ sub mtr_report_test_failed ($) {
{ {
print "[ fail ] timeout\n"; print "[ fail ] timeout\n";
} }
elsif ( $tinfo->{'ndb_test'} and $::cluster->[0]->{'installed_ok'} eq "NO")
{
print "[ fail ] ndbcluster start failure\n";
return;
}
else else
{ {
print "[ fail ]\n"; print "[ fail ]\n";
...@@ -144,6 +176,8 @@ sub mtr_report_stats ($) { ...@@ -144,6 +176,8 @@ sub mtr_report_stats ($) {
my $tot_passed= 0; my $tot_passed= 0;
my $tot_failed= 0; my $tot_failed= 0;
my $tot_tests= 0; my $tot_tests= 0;
my $tot_restarts= 0;
my $found_problems= 0; # Some warnings are errors...
foreach my $tinfo (@$tests) foreach my $tinfo (@$tests)
{ {
...@@ -161,6 +195,10 @@ sub mtr_report_stats ($) { ...@@ -161,6 +195,10 @@ sub mtr_report_stats ($) {
$tot_tests++; $tot_tests++;
$tot_failed++; $tot_failed++;
} }
if ( $tinfo->{'restarted'} )
{
$tot_restarts++;
}
} }
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
...@@ -183,41 +221,69 @@ sub mtr_report_stats ($) { ...@@ -183,41 +221,69 @@ sub mtr_report_stats ($) {
"the documentation at\n", "the documentation at\n",
"http://www.mysql.com/doc/en/MySQL_test_suite.html\n"; "http://www.mysql.com/doc/en/MySQL_test_suite.html\n";
} }
print
"The servers were restarted $tot_restarts times\n";
if ( $::opt_timer )
{
print
"Spent $::glob_tot_real_time seconds actually executing testcases\n"
}
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# If a debug run, there might be interesting information inside
# the "var/log/*.err" files. We save this info in "var/log/warnings"
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
if ( ! $::glob_use_running_server ) if ( ! $::glob_use_running_server )
{ {
# Save and report if there was any fatal warnings/errors in err logs
# Report if there was any fatal warnings/errors in the log files my $warnlog= "$::opt_vardir/log/warnings";
#
unlink("$::opt_vardir/log/warnings");
unlink("$::opt_vardir/log/warnings.tmp");
# Remove some non fatal warnings from the log files
# FIXME what is going on ????? ;-) unless ( open(WARN, ">$warnlog") )
# sed -e 's!Warning: Table:.* on delete!!g' -e 's!Warning: Setting lower_case_table_names=2!!g' -e 's!Warning: One can only use the --user.*root!!g' \ {
# var/log/*.err \ mtr_warning("can't write to the file \"$warnlog\": $!");
# | sed -e 's!Warning: Table:.* on rename!!g' \ }
# > var/log/warnings.tmp; else
# {
# found_error=0; # We report different types of problems in order
# # Find errors foreach my $pattern ( "^Warning:", "^Error:", "^==.* at 0x",
# for i in "^Warning:" "^Error:" "^==.* at 0x" "InnoDB: Warning", "missing DBUG_RETURN",
# do "mysqld: Warning",
# if ( $GREP "$i" var/log/warnings.tmp >> var/log/warnings ) "Attempting backtrace", "Assertion .* failed" )
# { {
# found_error=1 foreach my $errlog ( sort glob("$::opt_vardir/log/*.err") )
# } {
# done unless ( open(ERR, $errlog) )
# unlink("$::opt_vardir/log/warnings.tmp"); {
# if ( $found_error= "1" ) mtr_warning("can't read $errlog");
# { next;
# print "WARNING: Got errors/warnings while running tests. Please examine\n" }
# print "$::opt_vardir/log/warnings for details.\n" while ( <ERR> )
# } {
# } # Skip some non fatal warnings from the log files
if ( /Warning:\s+Table:.* on (delete|rename)/ or
/Warning:\s+Setting lower_case_table_names=2/ or
/Warning:\s+One can only use the --user.*root/ or
/InnoDB: Warning: we did not need to do crash recovery/)
{
next; # Skip these lines
}
if ( /$pattern/ )
{
$found_problems= 1;
print WARN $_;
}
}
}
}
if ( $found_problems )
{
mtr_warning("Got errors/warnings while running tests, please examine",
"\"$warnlog\" for details.");
}
}
} }
print "\n"; print "\n";
...@@ -235,6 +301,9 @@ sub mtr_report_stats ($) { ...@@ -235,6 +301,9 @@ sub mtr_report_stats ($) {
} }
} }
print "\n"; print "\n";
}
if ( $tot_failed != 0 || $found_problems)
{
mtr_error("there where failing test cases"); mtr_error("there where failing test cases");
} }
} }
...@@ -298,5 +367,11 @@ sub mtr_debug (@) { ...@@ -298,5 +367,11 @@ sub mtr_debug (@) {
print STDERR "####: ",join(" ", @_),"\n"; print STDERR "####: ",join(" ", @_),"\n";
} }
} }
sub mtr_verbose (@) {
if ( $::opt_verbose )
{
print STDERR "> ",join(" ", @_),"\n";
}
}
1; 1;
# -*- cperl -*-
# This is a library file used by the Perl version of mysql-test-run,
# and is part of the translation of the Bourne shell script with the
# same name.
use strict;
use File::Spec;
# These are not to be prefixed with "mtr_"
sub run_stress_test ();
##############################################################################
#
# Run tests in the stress mode
#
##############################################################################
sub run_stress_test ()
{
my $args;
my $stress_suitedir;
mtr_report("Starting stress testing\n");
if ( ! $::glob_use_embedded_server )
{
if ( ! mysqld_start($::master->[0],[],[]) )
{
mtr_error("Can't start the mysqld server");
}
}
my $stress_basedir=File::Spec->catdir($::opt_vardir, "stress");
#Clean up stress dir
if ( -d $stress_basedir )
{
rmtree($stress_basedir);
}
mkpath($stress_basedir);
if ($::opt_stress_suite ne 'main' && $::opt_stress_suite ne 'default' )
{
$stress_suitedir=File::Spec->catdir($::glob_mysql_test_dir, "suite",
$::opt_stress_suite);
}
else
{
$stress_suitedir=$::glob_mysql_test_dir;
}
if ( -d $stress_suitedir )
{
#$stress_suite_t_dir=File::Spec->catdir($stress_suitedir, "t");
#$stress_suite_r_dir=File::Spec->catdir($stress_suitedir, "r");
#FIXME: check dirs above for existence to ensure that test suite
# contains tests and results dirs
}
else
{
mtr_error("Specified test suite $::opt_stress_suite doesn't exist");
}
if ( @::opt_cases )
{
$::opt_stress_test_file=File::Spec->catfile($stress_basedir, "stress_tests.txt");
open(STRESS_FILE, ">$::opt_stress_test_file");
print STRESS_FILE join("\n",@::opt_cases),"\n";
close(STRESS_FILE);
}
elsif ( $::opt_stress_test_file )
{
$::opt_stress_test_file=File::Spec->catfile($stress_suitedir,
$::opt_stress_test_file);
if ( ! -f $::opt_stress_test_file )
{
mtr_error("Specified file $::opt_stress_test_file with list of tests does not exist\n",
"Please ensure that file exists and has proper permissions");
}
}
else
{
$::opt_stress_test_file=File::Spec->catfile($stress_suitedir,
"stress_tests.txt");
if ( ! -f $::opt_stress_test_file )
{
mtr_error("Default file $::opt_stress_test_file with list of tests does not exist\n",
"Please use --stress-test-file option to specify custom one or you can\n",
"just specify name of test for testing as last argument in command line");
}
}
if ( $::opt_stress_init_file )
{
$::opt_stress_init_file=File::Spec->catfile($stress_suitedir,
$::opt_stress_init_file);
if ( ! -f $::opt_stress_init_file )
{
mtr_error("Specified file $::opt_stress_init_file with list of tests does not exist\n",
"Please ensure that file exists and has proper permissions");
}
}
else
{
$::opt_stress_init_file=File::Spec->catfile($stress_suitedir,
"stress_init.txt");
if ( ! -f $::opt_stress_init_file )
{
$::opt_stress_init_file='';
}
}
if ( $::opt_stress_mode ne 'random' && $::opt_stress_mode ne 'seq' )
{
mtr_error("You specified wrong mode $::opt_stress_mode for stress test\n",
"Correct values are 'random' or 'seq'");
}
mtr_init_args(\$args);
mtr_add_arg($args, "--server-socket=%s", $::master->[0]->{'path_mysock'});
mtr_add_arg($args, "--server-user=%s", $::opt_user);
mtr_add_arg($args, "--server-database=%s", "test");
mtr_add_arg($args, "--stress-suite-basedir=%s", $::glob_mysql_test_dir);
mtr_add_arg($args, "--suite=%s", $::opt_stress_suite);
mtr_add_arg($args, "--stress-tests-file=%s", $::opt_stress_test_file);
mtr_add_arg($args, "--stress-basedir=%s", $stress_basedir);
mtr_add_arg($args, "--server-logs-dir=%s", $stress_basedir);
mtr_add_arg($args, "--stress-mode=%s", $::opt_stress_mode);
mtr_add_arg($args, "--mysqltest=%s", $::exe_mysqltest);
mtr_add_arg($args, "--threads=%s", $::opt_stress_threads);
mtr_add_arg($args, "--verbose");
mtr_add_arg($args, "--cleanup");
mtr_add_arg($args, "--log-error-details");
mtr_add_arg($args, "--abort-on-error");
if ( $::opt_stress_init_file )
{
mtr_add_arg($args, "--stress-init-file=%", $::opt_stress_init_file);
}
if ( !$::opt_stress_loop_count && !$::opt_stress_test_count &&
!$::opt_stress_test_duration )
{
#Limit stress testing with 20 loops in case when any limit parameter
#was specified
$::opt_stress_test_count=20;
}
if ( $::opt_stress_loop_count )
{
mtr_add_arg($args, "--loop-count=%s", $::opt_stress_loop_count);
}
if ( $::opt_stress_test_count )
{
mtr_add_arg($args, "--test-count=%s", $::opt_stress_test_count);
}
if ( $::opt_stress_test_duration )
{
mtr_add_arg($args, "--test-duration=%s", $::opt_stress_test_duration);
}
#Run stress test
mtr_run("$::glob_mysql_test_dir/mysql-stress-test.pl", $args, "", "", "", "");
if ( ! $::glob_use_embedded_server )
{
stop_masters();
}
}
1;
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -163,7 +163,7 @@ mysqltest: At line 1: Invalid argument to error: '1sssss' - the errno may only c ...@@ -163,7 +163,7 @@ mysqltest: At line 1: Invalid argument to error: '1sssss' - the errno may only c
mysqltest: At line 1: The sqlstate must be exactly 5 chars long mysqltest: At line 1: The sqlstate must be exactly 5 chars long
mysqltest: At line 1: The sqlstate may only consist of digits[0-9] and _uppercase_ letters mysqltest: At line 1: The sqlstate may only consist of digits[0-9] and _uppercase_ letters
mysqltest: At line 1: The sqlstate must be exactly 5 chars long mysqltest: At line 1: The sqlstate must be exactly 5 chars long
mysqltest: At line 1: Not available in mysqltest for MySQL 4.1.22 mysqltest: At line 1: Not available in this version of mysqltest
mysqltest: At line 1: Invalid argument to error: '999e9' - the errno may only consist of digits[0-9] mysqltest: At line 1: Invalid argument to error: '999e9' - the errno may only consist of digits[0-9]
mysqltest: At line 1: Invalid argument to error: '9b' - the errno may only consist of digits[0-9] mysqltest: At line 1: Invalid argument to error: '9b' - the errno may only consist of digits[0-9]
mysqltest: At line 1: Too many errorcodes specified mysqltest: At line 1: Too many errorcodes specified
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment