From dd6356acca1cb6aaca995c6d3619af89681aef99 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Fri, 20 Mar 2009 16:39:06 +0100 Subject: [PATCH 01/18] Bug #43074 MTR2 is not accessing core dumps when a path is too long Executable path is truncated in core If we see truncated path, try to guess using strings and grep If that doesn't work either, use known mysqld path --- mysql-test/lib/My/CoreDump.pm | 36 ++++++++++++++++++++++++++++++++--- mysql-test/mysql-test-run.pl | 3 ++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/mysql-test/lib/My/CoreDump.pm b/mysql-test/lib/My/CoreDump.pm index f3e9f521384..0591602d365 100644 --- a/mysql-test/lib/My/CoreDump.pm +++ b/mysql-test/lib/My/CoreDump.pm @@ -22,6 +22,33 @@ use My::Platform; use File::Temp qw/ tempfile tempdir /; +my $hint_mysqld; # Last resort guess for executable path + +# If path in core file is 79 chars we assume it's been truncated +# Looks like we can still find the full path using 'strings' +# If that doesn't work, use the hint (mysqld path) as last resort. + +sub _verify_binpath { + my ($binary, $core_name)= @_; + my $binpath; + + if (length $binary != 79) { + $binpath= $binary; + print "Core generated by '$binpath'\n"; + } else { + # Last occurrence of path ending in /mysql*, cut from first / + if (`strings '$core_name' | grep "/mysql[^/. ]*\$" | tail -1` =~ /(\/.*)/) { + $binpath= $1; + print "Guessing that core was generated by '$binpath'\n"; + } else { + return unless $hint_mysqld; + $binpath= $hint_mysqld; + print "Wild guess that core was generated by '$binpath'\n"; + } + } + return $binpath; +} + sub _gdb { my ($core_name)= @_; @@ -33,7 +60,8 @@ sub _gdb { `gdb -c '$core_name' --batch 2>&1` =~ /Core was generated by `([^\s\'\`]+)/; my $binary= $1 or return; - print "Core generated by '$binary'\n"; + + $binary= _verify_binpath ($binary, $core_name) or return; # Create tempfile containing gdb commands my ($tmp, $tmp_name) = tempfile(); @@ -73,7 +101,8 @@ sub _dbx { `echo | dbx - '$core_name' 2>&1` =~ /Corefile specified executable: "([^"]+)"/; my $binary= $1 or return; - print "Core generated by '$binary'\n"; + + $binary= _verify_binpath ($binary, $core_name) or return; # Find all threads my @thr_ids = `echo threads | dbx '$binary' '$core_name' 2>&1` =~ /t@\d+/g; @@ -225,7 +254,8 @@ EOF sub show { - my ($class, $core_name)= @_; + my ($class, $core_name, $exe_mysqld)= @_; + $hint_mysqld= $exe_mysqld; # On Windows, rely on cdb to be there... if (IS_WINDOWS) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index c5a68662d5b..221cbedc89f 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -429,6 +429,7 @@ sub run_test_server ($$$) { my $completed= []; my %running; my $result; + my $exe_mysqld= find_mysqld($basedir) || ""; # Used as hint to CoreDump my $suite_timeout_proc= My::SafeProcess->timer(suite_timeout()); @@ -500,7 +501,7 @@ sub run_test_server ($$$) { mtr_report(" - found '$core_name'", "($num_saved_cores/$opt_max_save_core)"); - My::CoreDump->show($core_file); + My::CoreDump->show($core_file, $exe_mysqld); if ($num_saved_cores >= $opt_max_save_core) { mtr_report(" - deleting it, already saved", From 37085905e5d8a718548c5192e88d85f18f09f878 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 24 Mar 2009 14:44:21 +0100 Subject: [PATCH 02/18] change order of cdb parameters to workaround a bug , where command (-c) is not evaluated if -i ,-y or -z contains an invalid path. cdb would hang then waiting for user input, which is bad for use in scripts --- mysql-test/lib/My/CoreDump.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/lib/My/CoreDump.pm b/mysql-test/lib/My/CoreDump.pm index 0591602d365..3ac9e385070 100644 --- a/mysql-test/lib/My/CoreDump.pm +++ b/mysql-test/lib/My/CoreDump.pm @@ -232,7 +232,7 @@ sub _cdb { my $cdb_cmd = "!sym prompts off; !analyze -v; .ecxr; !for_each_frame dv /t;!uniqstack -p;q"; my $cdb_output= - `cdb -z $core_name -i "$image_path" -y "$symbol_path" -t 0 -lines -c "$cdb_cmd" 2>&1`; + `cdb -c "$cdb_cmd" -z $core_name -i "$image_path" -y "$symbol_path" -t 0 -lines 2>&1`; return if $? >> 8; return unless $cdb_output; From fd6beee8d771d5f69ed50fba4e69a782351ccd75 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Fri, 27 Mar 2009 11:25:24 +0100 Subject: [PATCH 03/18] Bug #42807 MTR: './mtr somesuite.sometest' fails if somesuite is not in DEFAULT_SUITES If suite specified as part of test name, collect from that suite too. --- mysql-test/lib/mtr_cases.pm | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm index 23a85ef7ecc..09db2ad2ca6 100644 --- a/mysql-test/lib/mtr_cases.pm +++ b/mysql-test/lib/mtr_cases.pm @@ -119,11 +119,22 @@ sub collect_test_cases ($$) { if ( $test->{name} =~ /.*\.$tname/ ) { $found= 1; + last; } } if ( not $found ) { - mtr_error("Could not find '$tname' in '$suites' suite(s)"); + mtr_error("Could not find '$tname' in '$suites' suite(s)") unless $sname; + # If suite was part of name, find it there + my ($this_case) = collect_one_suite($sname, [ $tname ]); + if ($this_case) + { + push (@$cases, $this_case); + } + else + { + mtr_error("Could not find '$tname' in '$sname' suite"); + } } } } From de34d942d31d1f89d5c8b164e434706a40e12ff7 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 31 Mar 2009 13:15:52 +0200 Subject: [PATCH 04/18] Bug #43840 "too many tests failed" includes retries Only count non-retried tests, and increment before testing --- mysql-test/mysql-test-run.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 221cbedc89f..0cd8b9a703d 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -516,6 +516,7 @@ sub run_test_server ($$$) { } } $num_saved_datadir++; + $num_failed_test++ unless $result->{retries}; if ( !$opt_force ) { # Test has failed, force is off @@ -530,7 +531,6 @@ sub run_test_server ($$$) { "Terminating..."); return undef; } - $num_failed_test++; } # Retry test run after test failure From beb0eaa9d114448353279388de7f448119a50233 Mon Sep 17 00:00:00 2001 From: Magnus Svensson Date: Tue, 31 Mar 2009 15:39:40 +0200 Subject: [PATCH 05/18] Bug#43983 Support force restart of all servers after test ended - Some tests need to modify the server(s) so much that a total restart of all servers are necessary after test. Make it possible for a test to signal it want mtr.pl to restart all servers. --- mysql-test/include/mtr_check.sql | 10 ++++++++++ mysql-test/mysql-test-run.pl | 26 +++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/mysql-test/include/mtr_check.sql b/mysql-test/include/mtr_check.sql index 12cb2c870a2..9db631a2615 100644 --- a/mysql-test/include/mtr_check.sql +++ b/mysql-test/include/mtr_check.sql @@ -57,3 +57,13 @@ BEGIN mysql.user; END|| + +-- +-- Procedure used by test case used to force all +-- servers to restart after testcase and thus skipping +-- check test case after test +-- +CREATE DEFINER=root@localhost PROCEDURE force_restart() +BEGIN + SELECT 1 INTO OUTFILE 'force_restart'; +END|| diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index ba426446075..499d928cc54 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3067,6 +3067,26 @@ sub find_analyze_request } +# The test can leave a file in var/tmp/ to signal +# that all servers should be restarted +sub restart_forced_by_test +{ + my $restart = 0; + foreach my $mysqld ( mysqlds() ) + { + my $datadir = $mysqld->value('datadir'); + my $force_restart_file = "$datadir/mtr/force_restart"; + if ( -f $force_restart_file ) + { + mtr_verbose("Restart of servers forced by test"); + $restart = 1; + last; + } + } + return $restart; +} + + # Return timezone value of tinfo or default value sub timezone { my ($tinfo)= @_; @@ -3235,7 +3255,11 @@ sub run_testcase ($) { if ( $res == 0 ) { my $check_res; - if ( $opt_check_testcases and + if ( restart_forced_by_test() ) + { + stop_all_servers(); + } + elsif ( $opt_check_testcases and $check_res= check_testcase($tinfo, "after")) { if ($check_res == 1) { From 523bfc8d01e7b47e581fe63d051b9dc67c73db63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Wed, 1 Apr 2009 10:59:10 +0200 Subject: [PATCH 06/18] Bug#43983 Support force restart of all servers after test ended - Update testcases funcs_1.is_routines to only query information_schema.routines about the routines in 'test' database(where it has created it's routines) --- mysql-test/suite/funcs_1/datadict/is_routines.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/funcs_1/datadict/is_routines.inc b/mysql-test/suite/funcs_1/datadict/is_routines.inc index 573967cbc1b..19f7ed5d230 100644 --- a/mysql-test/suite/funcs_1/datadict/is_routines.inc +++ b/mysql-test/suite/funcs_1/datadict/is_routines.inc @@ -96,10 +96,11 @@ CREATE FUNCTION function_for_routines() RETURNS INT RETURN 0; SELECT specific_name,routine_catalog,routine_schema,routine_name,routine_type, routine_body,external_name,external_language,parameter_style,sql_path FROM information_schema.routines -WHERE routine_catalog IS NOT NULL OR external_name IS NOT NULL +WHERE routine_schema = 'test' AND + (routine_catalog IS NOT NULL OR external_name IS NOT NULL OR external_language IS NOT NULL OR sql_path IS NOT NULL OR routine_body <> 'SQL' OR parameter_style <> 'SQL' - OR specific_name <> routine_name; + OR specific_name <> routine_name); DROP PROCEDURE sp_for_routines; DROP FUNCTION function_for_routines; From ce1a3d16563f78ceef617e997299b45b79be233c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Wed, 1 Apr 2009 11:33:36 +0200 Subject: [PATCH 07/18] Bug#43983 Support force restart of all servers after test ended - Properly update .result file --- mysql-test/suite/funcs_1/r/is_routines.result | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/funcs_1/r/is_routines.result b/mysql-test/suite/funcs_1/r/is_routines.result index e7a900f5c0b..14a7107778c 100644 --- a/mysql-test/suite/funcs_1/r/is_routines.result +++ b/mysql-test/suite/funcs_1/r/is_routines.result @@ -111,10 +111,11 @@ CREATE FUNCTION function_for_routines() RETURNS INT RETURN 0; SELECT specific_name,routine_catalog,routine_schema,routine_name,routine_type, routine_body,external_name,external_language,parameter_style,sql_path FROM information_schema.routines -WHERE routine_catalog IS NOT NULL OR external_name IS NOT NULL +WHERE routine_schema = 'test' AND +(routine_catalog IS NOT NULL OR external_name IS NOT NULL OR external_language IS NOT NULL OR sql_path IS NOT NULL OR routine_body <> 'SQL' OR parameter_style <> 'SQL' - OR specific_name <> routine_name; + OR specific_name <> routine_name); specific_name routine_catalog routine_schema routine_name routine_type routine_body external_name external_language parameter_style sql_path DROP PROCEDURE sp_for_routines; DROP FUNCTION function_for_routines; From ba70e8eac7fad5904f16d31192d51e7145b07dcf Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 1 Apr 2009 13:58:30 +0200 Subject: [PATCH 08/18] Bug #43917 MTR2 does not report accurate test statistics when using the 'repeat=n' option In practice, only the last run of the test was counted Add a separate counter rep_failures for failures before last run --- mysql-test/lib/mtr_report.pm | 13 ++++++++++++- mysql-test/mysql-test-run.pl | 4 +++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm index 9c6ab35ee5e..1c08244bfc9 100644 --- a/mysql-test/lib/mtr_report.pm +++ b/mysql-test/lib/mtr_report.pm @@ -257,6 +257,17 @@ sub mtr_report_stats ($) { $tot_restarts++; } + # Add counts for repeated runs, if any. + # Note that the last run has already been counted above. + my $num_repeat = $tinfo->{'repeat'} - 1; + if ( $num_repeat > 0 ) + { + $tot_tests += $num_repeat; + my $rep_failed = $tinfo->{'rep_failures'} || 0; + $tot_failed += $rep_failed; + $tot_passed += $num_repeat - $rep_failed; + } + # Look for warnings produced by mysqltest my $base_file= mtr_match_extension($tinfo->{'result_file'}, "result"); # Trim extension @@ -336,7 +347,7 @@ sub mtr_report_stats ($) { foreach my $tinfo (@$tests) { my $tname= $tinfo->{'name'}; - if ( $tinfo->{failures} and ! $seen{$tname}) + if ( ($tinfo->{failures} || $tinfo->{rep_failures}) and ! $seen{$tname}) { print " $tname"; $seen{$tname}= 1; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 94e865ad5c8..5171dfef046 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -555,9 +555,11 @@ sub run_test_server ($$$) { # Repeat test $opt_repeat number of times my $repeat= $result->{repeat} || 1; - if ($repeat < $opt_repeat) + # Don't repeat if test was skipped + if ($repeat < $opt_repeat && $result->{'result'} ne 'MTR_RES_SKIPPED') { $result->{retries}= 0; + $result->{rep_failures}++ if $result->{failures}; $result->{failures}= 0; delete($result->{result}); $result->{repeat}= $repeat+1; From c3a43bcaa6eb7b252b83d8746504529c38361126 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 1 Apr 2009 16:23:10 +0200 Subject: [PATCH 09/18] Bug #43570 MTR2 hangs when test fails and named pipe created Hangs when trying to copy the pipe Amend copytree() to only copy regular files --- mysql-test/lib/My/File/Path.pm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/lib/My/File/Path.pm b/mysql-test/lib/My/File/Path.pm index 99edeecdaf7..25a26568eee 100644 --- a/mysql-test/lib/My/File/Path.pm +++ b/mysql-test/lib/My/File/Path.pm @@ -164,6 +164,9 @@ sub copytree { copytree("$from_dir/$_", "$to_dir/$_"); next; } + + # Only copy plain files + next unless -f "$from_dir/$_"; copy("$from_dir/$_", "$to_dir/$_"); } closedir(DIR); From edbfc0cb16b5906785e49b2826b9bcf37fd78f70 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 2 Apr 2009 13:00:44 +0200 Subject: [PATCH 10/18] Bug #42507 mtr2: the --check is fooled up by a code executed in --init_file mtr *thinks* there's a side effect Use the new force_restart() to avoid running the check-testcase --- mysql-test/r/init_file.result | 3 +++ mysql-test/t/init_file.test | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/init_file.result b/mysql-test/r/init_file.result index 8e014815a9c..43ed908ad01 100644 --- a/mysql-test/r/init_file.result +++ b/mysql-test/r/init_file.result @@ -4,6 +4,7 @@ SELECT * INTO @Y FROM init_file.startup limit 1,1; SELECT YEAR(@X)-YEAR(@Y); YEAR(@X)-YEAR(@Y) 0 +DROP DATABASE init_file; ok end of 4.1 tests select * from t1; @@ -19,3 +20,5 @@ y 3 11 13 +drop table t1, t2; +call mtr.force_restart(); diff --git a/mysql-test/t/init_file.test b/mysql-test/t/init_file.test index ceb5cae9743..7eb5381651d 100644 --- a/mysql-test/t/init_file.test +++ b/mysql-test/t/init_file.test @@ -14,7 +14,7 @@ SELECT * INTO @X FROM init_file.startup limit 0,1; SELECT * INTO @Y FROM init_file.startup limit 1,1; SELECT YEAR(@X)-YEAR(@Y); # Enable this DROP DATABASE only after resolving bug #42507 -# DROP DATABASE init_file; +DROP DATABASE init_file; --echo ok --echo end of 4.1 tests @@ -28,4 +28,9 @@ select * from t1; # 30, 3, 11, 13 select * from t2; # Enable this DROP TABLE only after resolving bug #42507 -#drop table t1, t2; +drop table t1, t2; + +# MTR will restart server anyway, but by forcing it we avoid being warned +# about the apparent side effect + +call mtr.force_restart(); From c43af0353e5db4035d54b65a9a1f58b7dff1d0d6 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 8 Apr 2009 14:54:36 +0200 Subject: [PATCH 11/18] Bug #41649 sporadic pb failure: mtr stopped, message "TIMEOUT (1200 seconds), ABORTING." Potentially infinite loop in check_expected_crash_and_restart Replace with finite loop and some additional logic --- mysql-test/lib/My/SafeProcess.pm | 18 ++++++++++++ mysql-test/mysql-test-run.pl | 50 ++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm index 5ef3286ad8e..17868a92c66 100644 --- a/mysql-test/lib/My/SafeProcess.pm +++ b/mysql-test/lib/My/SafeProcess.pm @@ -537,6 +537,24 @@ sub wait_any { } # +# Check if any process has exited, but don't wait. +# +# Returns a reference to the SafeProcess that +# exited or undefined +# +sub check_any { + for my $proc (values %running){ + if ( $proc->is_child($$) ) { + if (not $proc->wait_one(0)) { + _verbose ("Found exited $proc"); + return $proc; + } + } + } + return undef; +} + + # Overload string operator # and fallback to default functions if no # overloaded function is found diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5171dfef046..808d7fab165 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3286,10 +3286,38 @@ sub run_testcase ($) { } my $test= start_mysqltest($tinfo); + # Set only when we have to keep waiting after expectedly died server + my $keep_waiting_proc = 0; while (1) { - my $proc= My::SafeProcess->wait_any(); + my $proc; + if ($keep_waiting_proc) + { + # Any other process exited? + $proc = My::SafeProcess->check_any(); + if ($proc) + { + mtr_verbose ("Found exited process $proc"); + # If that was the timeout, cancel waiting + if ( $proc eq $test_timeout_proc ) + { + $keep_waiting_proc = 0; + } + } + else + { + $proc = $keep_waiting_proc; + } + } + else + { + $proc= My::SafeProcess->wait_any(); + } + + # Will be restored if we need to keep waiting + $keep_waiting_proc = 0; + unless ( defined $proc ) { mtr_error("wait_any failed"); @@ -3385,8 +3413,12 @@ sub run_testcase ($) { # ---------------------------------------------------- # Check if it was an expected crash # ---------------------------------------------------- - if ( check_expected_crash_and_restart($proc) ) + my $check_crash = check_expected_crash_and_restart($proc); + if ($check_crash) { + # Keep waiting if it returned 2, if 1 don't wait or stop waiting. + $keep_waiting_proc = 0 if $check_crash == 1; + $keep_waiting_proc = $proc if $check_crash == 2; next; } @@ -3727,16 +3759,16 @@ sub check_expected_crash_and_restart { { mtr_verbose("Crash was expected, file '$expect_file' exists"); - while (1){ - + for (my $waits = 0; $waits < 50; $waits++) + { # If last line in expect file starts with "wait" # sleep a little and try again, thus allowing the # test script to control when the server should start - # up again + # up again. Keep trying for up to 5s at a time. my $last_line= mtr_lastlinesfromfile($expect_file, 1); if ($last_line =~ /^wait/ ) { - mtr_verbose("Test says wait before restart"); + mtr_verbose("Test says wait before restart") if $waits == 0; mtr_milli_sleep(100); next; } @@ -3746,11 +3778,11 @@ sub check_expected_crash_and_restart { # Start server with same settings as last time mysqld_start($mysqld, $mysqld->{'started_opts'}); - last; + return 1; } + # Loop ran through: we should keep waiting after a re-check + return 2; } - - return 1; } # Not an expected crash From 2943d2b7e99b4221ef481890fc9a56ad569f3985 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 23 Apr 2009 13:35:02 +0200 Subject: [PATCH 12/18] Bug #42804 --parallel option does not work for MTR under ActiveState perl The problem here was the method how MTR gets its unique thread ids. Prior to this patch, the method to do it was to maintain a global table of pid,mtr_unique_id) pairs. The table was backed by a text file. The table was cleaned up one in a while and dead processes leaking unique_ids were determined with with kill(0) or with scripting tasklist on Windows. This method is flawed specifically on native Windows Perl. fork() is implemented with starting a new thread, give it a syntetic negative PID (threadID*(-1)), until this thread creates a new process with exec() However, neither tasklist nor any other native Windows tool can cope with negative perl PIDs. This lead to incorrect determination of dead process and reusing already used mtr_unique_id. The patch introduces alternative portable method of solving unique-id problem. When a process needs a unique id in range [min...max], it just starts to open files named min, min+1,...max in a loop . After file is opened, we do non-blocking flock(). When flock() succeeds, process has allocated the ID. When process dies, file is unlocked . Checks for zombies are not necessary. Since the change would create a co-existence problems with older version of MTR, because of different way to calculate IDs, the default ID range is changed from 250-299 to 300-349. Another fix that was necessary enable --parallel option was to serialize spawn() calls on Windows. specifically, IO redirects needed to be protected. This patch also fixes hanging CRTL-C (as described in Bug #38629) for the "new" MTR. The fix was already in 6.0 and is now downported. --- mysql-test/lib/My/SafeProcess/Base.pm | 9 ++ mysql-test/lib/mtr_report.pm | 11 +- mysql-test/lib/mtr_unique.pm | 186 ++++++++------------------ mysql-test/mysql-test-run.pl | 11 +- 4 files changed, 85 insertions(+), 132 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess/Base.pm b/mysql-test/lib/My/SafeProcess/Base.pm index 3fc1b1be017..9a6871264b8 100644 --- a/mysql-test/lib/My/SafeProcess/Base.pm +++ b/mysql-test/lib/My/SafeProcess/Base.pm @@ -83,6 +83,13 @@ sub exit_status { }; } +# threads.pm may not exist everywhere, so use only on Windows. + +use if $^O eq "MSWin32", "threads"; +use if $^O eq "MSWin32", "threads::shared"; + +my $win32_spawn_lock :shared; + # # Create a new process @@ -104,6 +111,8 @@ sub create_process { if ($^O eq "MSWin32"){ + lock($win32_spawn_lock); + #printf STDERR "stdin %d, stdout %d, stderr %d\n", # fileno STDIN, fileno STDOUT, fileno STDERR; diff --git a/mysql-test/lib/mtr_report.pm b/mysql-test/lib/mtr_report.pm index 1c08244bfc9..93463d88d74 100644 --- a/mysql-test/lib/mtr_report.pm +++ b/mysql-test/lib/mtr_report.pm @@ -30,6 +30,8 @@ our @EXPORT= qw(report_option mtr_print_line mtr_print_thick_line mtr_report_test); use mtr_match; +use My::Platform; +use POSIX qw[ _exit ]; require "mtr_io.pl"; my $tot_real_time= 0; @@ -470,7 +472,14 @@ sub mtr_warning (@) { sub mtr_error (@) { print STDERR _name(), _timestamp(), "mysql-test-run: *** ERROR: ", join(" ", @_), "\n"; - exit(1); + if (IS_WINDOWS) + { + POSIX::_exit(1); + } + else + { + exit(1); + } } diff --git a/mysql-test/lib/mtr_unique.pm b/mysql-test/lib/mtr_unique.pm index 294a5d7b4d6..6b60157422d 100644 --- a/mysql-test/lib/mtr_unique.pm +++ b/mysql-test/lib/mtr_unique.pm @@ -28,32 +28,36 @@ sub msg { # print "### unique($$) - ", join(" ", @_), "\n"; } -my $file; +my $dir; if(!IS_WINDOWS) { - $file= "/tmp/mysql-test-ports"; + $dir= "/tmp/mysql-unique-ids"; } else { - $file= $ENV{'TEMP'}."/mysql-test-ports"; -} - - -my %mtr_unique_ids; - -END { - my $allocated_id= $mtr_unique_ids{$$}; - if (defined $allocated_id) + # Try to use machine-wide directory location for unique IDs, + # $ALLUSERSPROFILE . IF it is not available, fallback to $TEMP + # which is typically a per-user temporary directory + if (exists $ENV{'ALLUSERSPROFILE'} && -w $ENV{'ALLUSERSPROFILE'}) { - mtr_release_unique_id($allocated_id); + $dir= $ENV{'ALLUSERSPROFILE'}."/mysql-unique-ids"; } - delete $mtr_unique_ids{$$}; + else + { + $dir= $ENV{'TEMP'}."/mysql-unique-ids"; + } +} + +my $mtr_unique_fh = undef; + +END +{ + mtr_release_unique_id(); } # -# Get a unique, numerical ID, given a file name (where all -# requested IDs are stored), a minimum and a maximum value. +# Get a unique, numerical ID in a specified range. # # If no unique ID within the specified parameters can be # obtained, return undef. @@ -61,135 +65,63 @@ END { sub mtr_get_unique_id($$) { my ($min, $max)= @_;; - msg("get, '$file', $min-$max"); + msg("get $min-$max, $$"); - die "Can only get one unique id per process!" if $mtr_unique_ids{$$}; + die "Can only get one unique id per process!" if defined $mtr_unique_fh; - my $ret = undef; - my $changed = 0; - if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { - die 'lock file is a symbolic link'; - } + # Make sure our ID directory exists + if (! -d $dir) + { + # If there is a file with the reserved + # directory name, just delete the file. + if (-e $dir) + { + unlink($dir); + } - chmod 0777, "$file.sem"; - open SEM, ">", "$file.sem" or die "can't write to $file.sem"; - flock SEM, LOCK_EX or die "can't lock $file.sem"; - if(! -e $file) { - open FILE, ">", $file or die "can't create $file"; - close FILE; - } + mkdir $dir; + chmod 0777, $dir; - msg("HAVE THE LOCK"); - - if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { - die 'lock file is a symbolic link'; - } - - chmod 0777, $file; - open FILE, "+<", $file or die "can't open $file"; - #select undef,undef,undef,0.2; - seek FILE, 0, 0; - my %taken = (); - while() { - chomp; - my ($id, $pid) = split / /; - $taken{$id} = $pid; - msg("taken: $id, $pid"); - # Check if process with given pid is alive - if(!process_alive($pid)) { - print "Removing slot $id used by missing process $pid\n"; - msg("Removing slot $id used by missing process $pid"); - delete $taken{$id}; - $changed++; + if(! -d $dir) + { + die "can't make directory $dir"; } } - for(my $i=$min; $i<=$max; ++$i) { - if(! exists $taken{$i}) { - $ret = $i; - $taken{$i} = $$; - $changed++; - # Remember the id this process got - $mtr_unique_ids{$$}= $i; - msg(" got $i"); - last; + + + my $fh; + for(my $id = $min; $id <= $max; $id++) + { + open( $fh, ">$dir/$id"); + chmod 0666, "$dir/$id"; + # Try to lock the file exclusively. If lock succeeds, we're done. + if (flock($fh, LOCK_EX|LOCK_NB)) + { + # Store file handle - we would need it to release the ID (==unlock the file) + $mtr_unique_fh = $fh; + return $id; + } + else + { + close $fh; } } - if($changed) { - seek FILE, 0, 0; - truncate FILE, 0 or die "can't truncate $file"; - for my $k (keys %taken) { - print FILE $k . ' ' . $taken{$k} . "\n"; - } - } - close FILE; - - msg("RELEASING THE LOCK"); - flock SEM, LOCK_UN or warn "can't unlock $file.sem"; - close SEM; - - return $ret; + return undef; } # # Release a unique ID. # -sub mtr_release_unique_id($) { - my ($myid)= @_; - - msg("release, $myid"); - - - if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { - die 'lock file is a symbolic link'; - } - - open SEM, ">", "$file.sem" or die "can't write to $file.sem"; - flock SEM, LOCK_EX or die "can't lock $file.sem"; - - msg("HAVE THE LOCK"); - - if(eval("readlink '$file'") || eval("readlink '$file.sem'")) { - die 'lock file is a symbolic link'; - } - - if(! -e $file) { - open FILE, ">", $file or die "can't create $file"; - close FILE; - } - open FILE, "+<", $file or die "can't open $file"; - #select undef,undef,undef,0.2; - seek FILE, 0, 0; - my %taken = (); - while() { - chomp; - my ($id, $pid) = split / /; - msg(" taken, $id $pid"); - $taken{$id} = $pid; - } - - if ($taken{$myid} != $$) +sub mtr_release_unique_id() +{ + msg("release $$"); + if (defined $mtr_unique_fh) { - msg(" The unique id for this process does not match pid"); + close $mtr_unique_fh; + $mtr_unique_fh = undef; } - - - msg(" removing $myid"); - delete $taken{$myid}; - seek FILE, 0, 0; - truncate FILE, 0 or die "can't truncate $file"; - for my $k (keys %taken) { - print FILE $k . ' ' . $taken{$k} . "\n"; - } - close FILE; - - msg("RELEASE THE LOCK"); - - flock SEM, LOCK_UN or warn "can't unlock $file.sem"; - close SEM; - - delete $mtr_unique_ids{$$}; } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 808d7fab165..5805043df56 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1326,16 +1326,19 @@ sub set_build_thread_ports($) { if ( lc($opt_build_thread) eq 'auto' ) { my $found_free = 0; - $build_thread = 250; # Start attempts from here + $build_thread = 300; # Start attempts from here while (! $found_free) { - $build_thread= mtr_get_unique_id($build_thread, 299); + $build_thread= mtr_get_unique_id($build_thread, 349); if ( !defined $build_thread ) { - mtr_error("Could not get a unique build thread id"); + mtr_error("Could not get a unique build thread id"); } $found_free= check_ports_free($build_thread); # If not free, release and try from next number - mtr_release_unique_id($build_thread++) unless $found_free; + if (! $found_free) { + mtr_release_unique_id(); + $build_thread++; + } } } else From ca0e746d3b5a5e854dcf2a858d5cb9ce4d09d47a Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Tue, 28 Apr 2009 23:06:36 +0200 Subject: [PATCH 13/18] Bug #44530 mtr v2 startup very slow on Windows. MTR is stuck for about 20 seconds checking for free ports. The reason is that perl's connect() takes 1 second on windows if port is not opened. This patch fixes the mtr_ping_port implementation on Windows to use Net::Ping for the port checking with small (0.1sec) timeout. This patch also removes pointless second call to check_ports_free() in case of auto build thread. --- mysql-test/lib/mtr_process.pl | 22 ++++++++++++++++++++++ mysql-test/mysql-test-run.pl | 9 ++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index a99119a199d..25bbce84ab3 100644 --- a/mysql-test/lib/mtr_process.pl +++ b/mysql-test/lib/mtr_process.pl @@ -21,6 +21,9 @@ use strict; use Socket; use Errno; +use My::Platform; +use if IS_WINDOWS, "Net::Ping"; + sub sleep_until_file_created ($$$); sub mtr_ping_port ($); @@ -30,6 +33,25 @@ sub mtr_ping_port ($) { mtr_verbose("mtr_ping_port: $port"); + if (IS_WINDOWS) + { + # Under Windows, connect to a port that is not open is slow + # It takes ~1sec. Net::Ping with small timeout is much faster. + my $ping = Net::Ping->new(); + $ping->port_number($port); + + if ($ping->ping("localhost",0.1)) + { + mtr_verbose("USED"); + return 1; + } + else + { + mtr_verbose("FREE"); + return 0; + } + } + my $remote= "localhost"; my $iaddr= inet_aton($remote); if ( ! $iaddr ) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5805043df56..b1f0219616e 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1344,14 +1344,13 @@ sub set_build_thread_ports($) { else { $build_thread = $opt_build_thread + $thread - 1; + if (! check_ports_free($build_thread)) { + # Some port was not free(which one has already been printed) + mtr_error("Some port(s) was not free") + } } $ENV{MTR_BUILD_THREAD}= $build_thread; - if (! check_ports_free($build_thread)) { - # Some port was not free(which one has already been printed) - mtr_error("Some port(s) was not free") - } - # Calculate baseport $baseport= $build_thread * 10 + 10000; if ( $baseport < 5001 or $baseport + 9 >= 32767 ) From 261066e25155c6a84449f5ff31f7de187216ee71 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Wed, 29 Apr 2009 16:13:38 +0200 Subject: [PATCH 14/18] Bug #44511 MTR2: add an option not to kill other servers when one from the group exits MTR would die as soon as one server terminates Implemented --wait-all option and associated subroutine --- mysql-test/lib/My/SafeProcess.pm | 12 +++++++++++ mysql-test/mysql-test-run.pl | 35 ++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess.pm b/mysql-test/lib/My/SafeProcess.pm index 17868a92c66..7e102b628ca 100644 --- a/mysql-test/lib/My/SafeProcess.pm +++ b/mysql-test/lib/My/SafeProcess.pm @@ -536,6 +536,18 @@ sub wait_any { return $proc; } + +# +# Wait for all processes to exit +# +sub wait_all { + while(keys %running) + { + wait_any(); + } +} + + # # Check if any process has exited, but don't wait. # diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index b1f0219616e..5153a59135f 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -209,6 +209,7 @@ sub check_timeout { return $opt_testcase_timeout * 6; }; my $opt_start; my $opt_start_dirty; +my $opt_wait_all; my $opt_repeat= 1; my $opt_retry= 3; my $opt_retry_failure= 2; @@ -879,6 +880,7 @@ sub command_line_setup { 'sleep=i' => \$opt_sleep, 'start-dirty' => \$opt_start_dirty, 'start' => \$opt_start, + 'wait-all' => \$opt_wait_all, 'print-testcases' => \&collect_option, 'repeat=i' => \$opt_repeat, 'retry=i' => \$opt_retry, @@ -1236,6 +1238,15 @@ sub command_line_setup { } } + # -------------------------------------------------------------------------- + # Check use of wait-all + # -------------------------------------------------------------------------- + + if ($opt_wait_all && ! ($opt_start_dirty || $opt_start)) + { + mtr_error("--wait-all can only be used with --start or --start-dirty"); + } + # -------------------------------------------------------------------------- # Check timeout arguments # -------------------------------------------------------------------------- @@ -3258,19 +3269,29 @@ sub run_testcase ($) { # -------------------------------------------------------------------- # If --start or --start-dirty given, stop here to let user manually # run tests + # If --wait-all is also given, do the same, but don't die if one + # server exits # ---------------------------------------------------------------------- + if ( $opt_start or $opt_start_dirty ) { mtr_print("\nStarted", started(all_servers())); mtr_print("Waiting for server(s) to exit..."); - my $proc= My::SafeProcess->wait_any(); - if ( grep($proc eq $_, started(all_servers())) ) - { - mtr_print("Server $proc died"); + if ( $opt_wait_all ) { + My::SafeProcess->wait_all(); + mtr_print( "All servers exited" ); + exit(1); + } + else { + my $proc= My::SafeProcess->wait_any(); + if ( grep($proc eq $_, started(all_servers())) ) + { + mtr_print("Server $proc died"); + exit(1); + } + mtr_print("Unknown process $proc died"); exit(1); } - mtr_print("Unknown process $proc died"); - exit(1); } my $test_timeout_proc= My::SafeProcess->timer(testcase_timeout()); @@ -5153,6 +5174,8 @@ Misc options $0 --start alias & start-dirty Only start the servers (without initialization) for the first specified test case + wait-all If --start or --start-dirty option is used, wait for all + servers to exit before finishing the process fast Run as fast as possible, dont't wait for servers to shutdown etc. repeat=N Run each test N number of times From cf1845f4da8ae1208ca3da8e83329784099ae4f6 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Thu, 30 Apr 2009 13:23:36 +0200 Subject: [PATCH 15/18] Bug #44561 mtr2 --start-dirty is broken start-dirty would remove stored procs etc. Skip the copying back from stored system dir if using --start-dirty --- mysql-test/mysql-test-run.pl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 5153a59135f..9f08d0e3c32 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -3202,7 +3202,7 @@ sub run_testcase ($) { { # Remove old datadirs - clean_datadir(); + clean_datadir() unless $opt_start_dirty; # Restore old ENV while (my ($option, $value)= each( %old_env )) { @@ -4504,14 +4504,17 @@ sub start_servers($) { my $mysqld_basedir= $mysqld->value('basedir'); if ( $basedir eq $mysqld_basedir ) { - # Copy datadir from installed system db - for my $path ( "$opt_vardir", "$opt_vardir/..") { - my $install_db= "$path/install.db"; - copytree($install_db, $datadir) - if -d $install_db; + if (! $opt_start_dirty) # If dirty, keep possibly grown system db + { + # Copy datadir from installed system db + for my $path ( "$opt_vardir", "$opt_vardir/..") { + my $install_db= "$path/install.db"; + copytree($install_db, $datadir) + if -d $install_db; + } + mtr_error("Failed to copy system db to '$datadir'") + unless -d $datadir; } - mtr_error("Failed to copy system db to '$datadir'") - unless -d $datadir; } else { From 9e9d9bf9d7884b956455c46f5308205d85d5b130 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 8 May 2009 01:10:53 +0200 Subject: [PATCH 16/18] Fix regression on pushbuild 1. The cygwin perl is ancient there, and Net::Ping does not have port_number method (port_number was introduced around 2007). The fix is to check if port_number is present. Otherwise, fallback to the slow connect(). --- mysql-test/lib/mtr_process.pl | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/mysql-test/lib/mtr_process.pl b/mysql-test/lib/mtr_process.pl index 25bbce84ab3..a42627c93cd 100644 --- a/mysql-test/lib/mtr_process.pl +++ b/mysql-test/lib/mtr_process.pl @@ -24,7 +24,22 @@ use Errno; use My::Platform; use if IS_WINDOWS, "Net::Ping"; - +# Ancient perl might not have port_number method for Net::Ping. +# Check it and use fallback to connect() if it is not present. +BEGIN +{ + my $use_netping= 0; + if (IS_WINDOWS) + { + my $ping = Net::Ping->new(); + if ($ping->can("port_number")) + { + $use_netping= 1; + } + } + eval 'sub USE_NETPING { $use_netping }'; +} + sub sleep_until_file_created ($$$); sub mtr_ping_port ($); @@ -33,22 +48,21 @@ sub mtr_ping_port ($) { mtr_verbose("mtr_ping_port: $port"); - if (IS_WINDOWS) + if (IS_WINDOWS && USE_NETPING) { # Under Windows, connect to a port that is not open is slow # It takes ~1sec. Net::Ping with small timeout is much faster. my $ping = Net::Ping->new(); $ping->port_number($port); - if ($ping->ping("localhost",0.1)) { - mtr_verbose("USED"); - return 1; + mtr_verbose("USED"); + return 1; } else { - mtr_verbose("FREE"); - return 0; + mtr_verbose("FREE"); + return 0; } } From 2f9155296d30c8728b6d4dc8adea7b3d9b56d075 Mon Sep 17 00:00:00 2001 From: Bjorn Munch Date: Tue, 12 May 2009 14:53:46 +0200 Subject: [PATCH 17/18] Bug #42988 MTR2's --help output does not document --parallel Several options were not documented Added missing options and removed a few Fixed use of --skip-combinations --- mysql-test/lib/mtr_cases.pm | 4 ++-- mysql-test/mysql-test-run.pl | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/mysql-test/lib/mtr_cases.pm b/mysql-test/lib/mtr_cases.pm index 09db2ad2ca6..5a1946687a8 100644 --- a/mysql-test/lib/mtr_cases.pm +++ b/mysql-test/lib/mtr_cases.pm @@ -33,7 +33,7 @@ our $print_testcases; our $skip_rpl; our $do_test; our $skip_test; -our $opt_skip_combination; +our $skip_combinations; our $binlog_format; our $enable_disabled; our $default_storage_engine; @@ -386,7 +386,7 @@ sub collect_one_suite($) # Read combinations for this suite and build testcases x combinations # if any combinations exists # ---------------------------------------------------------------------- - if ( ! $opt_skip_combination ) + if ( ! $skip_combinations ) { my @combinations; my $combination_file= "$suitedir/combinations"; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 1b479cc938d..45ee54442d6 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -5063,10 +5063,13 @@ Options to control what engine/variation to run vs-config Visual Studio configuration used to create executables (default: MTR_VS_CONFIG environment variable) - config|defaults-file= Use fixed config template for all + defaults-file= Use fixed config template for all tests defaults_extra_file= Extra config template to add to all generated configs + combination= Use at least twice to run tests with specified + options to mysqld + skip-combinations Ignore combination file (or options) Options to control directories to use tmpdir=DIR The directory where temporary files are stored @@ -5089,7 +5092,6 @@ Options to control what test suites or cases to run force Continue to run the suite after failure with-ndbcluster-only Run only tests that include "ndb" in the filename skip-ndb[cluster] Skip all tests that need cluster - skip-ndb[cluster]-slave Skip all tests that need a slave cluster do-test=PREFIX or REGEX Run test cases which name are prefixed with PREFIX or fulfills REGEX @@ -5104,6 +5106,9 @@ Options to control what test suites or cases to run The default is: "$DEFAULT_SUITES" skip-rpl Skip the replication test cases. big-test Also run tests marked as "big" + enable-disabled Run also tests marked as disabled + print_testcases Don't run the tests but print details about all the + selected tests, in the order they would be run. Options that specify ports @@ -5172,7 +5177,7 @@ Options for valgrind valgrind-options=ARGS Deprecated, use --valgrind-option valgrind-option=ARGS Option to give valgrind, replaces default option(s), can be specified more then once - valgrind-path=[EXE] Path to the valgrind executable + valgrind-path= Path to the valgrind executable callgrind Instruct valgrind to use callgrind Misc options @@ -5180,6 +5185,7 @@ Misc options comment=STR Write STR to the output notimer Don't show test case execution time verbose More verbose output(use multiple times for even more) + verbose-restart Write when and why servers are restarted start Only initialize and start the servers, using the startup settings for the first specified test case Example: @@ -5190,6 +5196,7 @@ Misc options servers to exit before finishing the process fast Run as fast as possible, dont't wait for servers to shutdown etc. + parallel=N Run tests in N parallel threads (default=1) repeat=N Run each test N number of times retry=N Retry tests that fail N times, limit number of failures to $opt_retry_failure @@ -5207,6 +5214,12 @@ Misc options sleep=SECONDS Passed to mysqltest, will be used as fixed sleep time gcov Collect coverage information after the test. The result is a gcov file per source and header file. + experimental= Refer to list of tests considered experimental; + failures will be marked exp-fail instead of fail. + report-features First run a "test" that reports mysql features + timestamp Print timestamp before each test report line + timediff With --timestamp, also print time passed since + *previous* test started HERE exit(1); From e3bdffe10032b16564b9130d00094ffaa40b0777 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Thu, 14 May 2009 21:56:53 +0200 Subject: [PATCH 18/18] Bug #44775 MTR fails to bootstrap mysqld on Windows in Pushbuild 2. Suspected reason for the failure is that safe_process.exe already runs in a job that does not allow breakaways. The fix is to use a fallback - make newly created process the root of the new process group. This allows to kill process together with descendants via GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid) --- .../lib/My/SafeProcess/safe_process_win.cc | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/mysql-test/lib/My/SafeProcess/safe_process_win.cc b/mysql-test/lib/My/SafeProcess/safe_process_win.cc index 4fb89f098ed..80c1b7a97f2 100755 --- a/mysql-test/lib/My/SafeProcess/safe_process_win.cc +++ b/mysql-test/lib/My/SafeProcess/safe_process_win.cc @@ -259,22 +259,37 @@ int main(int argc, const char** argv ) the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag, making sure it will be terminated when the last handle to it is closed(which is owned by this process). + + If breakaway from job fails on some reason, fallback is to create a + new process group. Process groups also allow to kill process and its + descedants, subject to some restrictions (processes have to run within + the same console,and must not ignore CTRL_BREAK) */ - if (CreateProcess(NULL, (LPSTR)child_args, + DWORD create_flags[]= {CREATE_BREAKAWAY_FROM_JOB, CREATE_NEW_PROCESS_GROUP, 0}; + BOOL process_created= FALSE; + BOOL jobobject_assigned= FALSE; + + for (int i=0; i < sizeof(create_flags)/sizeof(create_flags[0]); i++) + { + process_created= CreateProcess(NULL, (LPSTR)child_args, NULL, NULL, TRUE, /* inherit handles */ - CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB, + CREATE_SUSPENDED | create_flags[i], NULL, NULL, &si, - &process_info) == 0) - die("CreateProcess failed"); + &process_info); + if (process_created) + { + jobobject_assigned= AssignProcessToJobObject(job_handle, process_info.hProcess); + break; + } + } - if (AssignProcessToJobObject(job_handle, process_info.hProcess) == 0) + if (!process_created) { - TerminateProcess(process_info.hProcess, 200); - die("AssignProcessToJobObject failed"); + die("CreateProcess failed"); } ResumeThread(process_info.hThread); CloseHandle(process_info.hThread); @@ -312,6 +327,13 @@ int main(int argc, const char** argv ) message("TerminateJobObject failed"); CloseHandle(job_handle); message("Job terminated and closed"); + + if (!jobobject_assigned) + { + GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, process_info.dwProcessId); + TerminateProcess(process_info.hProcess, 202); + } + if (wait_res != WAIT_OBJECT_0 + CHILD) { /* The child has not yet returned, wait for it */