3 package PerlACE
::Process
;
6 use POSIX
"sys_wait_h";
11 ###############################################################################
17 if (defined $Config{sig_name
}) {
19 foreach my $name (split (' ', $Config{sig_name
})) {
26 for ($i = 0; $i < 255; ++$i) {
31 ###############################################################################
33 ### Constructor and Destructor
38 my $class = ref ($proto) || $proto;
42 $self->{IGNOREEXESUBDIR
} = 0;
43 $self->{IGNOREHOSTROOT
} = 0;
44 $self->{PROCESS
} = undef;
45 $self->{EXECUTABLE
} = shift;
46 $self->{ARGUMENTS
} = shift;
47 $self->{VALGRIND_CMD
} = $ENV{'ACE_RUN_VALGRIND_CMD'};
49 if (!defined $PerlACE::Process
::WAIT_DELAY_FACTOR
) {
50 if (defined $self->{PURIFY_CMD
}) {
51 $PerlACE::Process
::WAIT_DELAY_FACTOR
= 10;
53 elsif (defined $self->{VALGRIND_CMD
}) {
54 $PerlACE::Process
::WAIT_DELAY_FACTOR
= 10;
57 $PerlACE::Process
::WAIT_DELAY_FACTOR
= 1;
61 bless ($self, $class);
69 if ($self->{RUNNING
} == 1) {
70 print STDERR
"ERROR: <", $self->{EXECUTABLE
},
71 "> still running upon object destruction\n";
75 if (defined $self->{SCRIPTFILE
}) {
76 unlink $self->{SCRIPTFILE
};
80 ###############################################################################
89 $self->{EXECUTABLE
} = shift;
92 my $executable = $self->{EXECUTABLE
};
94 if ($self->{IGNOREHOSTROOT
} == 0) {
95 if (PerlACE
::is_vxworks_test
()) {
96 $executable = PerlACE
::VX_HostFile
($executable);
100 if ($self->{IGNOREEXESUBDIR
}) {
104 my $basename = basename
($executable);
105 my $dirname = dirname
($executable).'/';
107 my $subdir = $PerlACE::Process
::ExeSubDir
;
108 if (defined $self->{TARGET
} && defined $self->{TARGET
}->{EXE_SUBDIR
}) {
109 $subdir = $self->{TARGET
}->{EXE_SUBDIR
};
112 $executable = $dirname . $subdir . $basename;
121 $self->{ARGUMENTS
} = shift;
124 return $self->{ARGUMENTS
};
130 my $tgt_pidfile = shift;
132 # If a filesystem mapping exists
133 if (defined $self->{TARGET
} && defined $self->{TARGET
}{TEST_FSROOT
} &&
134 defined $ENV{TEST_ROOT
}) {
135 my $pidfile = PerlACE
::rebase_path
($tgt_pidfile,
136 $self->{TARGET
}->{TEST_FSROOT
},
137 $self->{TARGET
}->{TEST_ROOT
});
138 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
139 print STDERR
"Checking for pid file $pidfile\n";
143 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
144 print STDERR
"Found mapped pid file\n";
146 if (open(PID
, "<$pidfile")) {
149 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
150 print STDERR
"Read $rc from mapped file\n";
156 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
157 print STDERR
"Could not open mapped pid file\n";
161 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
162 print STDERR
"Could not find mapped file " . basename
($pidfile) . "\n";
166 my $shell = $self->{TARGET
}->{REMOTE_SHELL
};
167 print STDERR
"trying to remote read PID from file $tgt_pidfile\n";
168 $rc = int(`$shell 'if [ -e $tgt_pidfile -a -s $tgt_pidfile ] ; then cat $tgt_pidfile; rm -f $tgt_pidfile >/dev/null 2>&1; else echo 0; fi'`);
177 my $exe = File
::Spec
->rel2abs ($self->Executable ());
178 my $cur_root = $ENV{TEST_ROOT
};
180 # Translate to target
181 if (defined $self->{TARGET
} && defined $ENV{TEST_ROOT
} &&
182 defined $self->{TARGET
}->{TEST_ROOT
}) {
183 $exe = PerlACE
::rebase_path
($exe,
185 $self->{TARGET
}->{TEST_ROOT
});
186 $cur_root = $self->{TARGET
}->{TEST_ROOT
};
189 # Translate to different filesystem
190 if (defined $self->{TARGET
} && defined $ENV{TEST_ROOT
} &&
191 defined $self->{TARGET
}->{TEST_FSROOT
}) {
192 $exe = PerlACE
::rebase_path
($exe,
194 $self->{TARGET
}->{TEST_FSROOT
});
196 } elsif (defined $self->{TARGET
} && defined $self->{TARGET
}->{TARGET_FSROOT
}) {
197 # If the target's config has a different filesystem root, rebase the executable
198 # from local root to the target's root.
199 $exe = File
::Spec
->rel2abs ($exe);
200 $exe = PerlACE
::rebase_path
($exe,
201 $self->{TARGET
}->{HOST_FSROOT
},
202 $self->{TARGET
}->{TARGET_FSROOT
});
205 my $commandline = $exe;
206 if (defined $self->{REMOTEINFO
}) {
207 my($method) = $self->{REMOTEINFO
}->{method
};
208 my($username) = $self->{REMOTEINFO
}->{username
};
209 my($remote) = $self->{REMOTEINFO
}->{hostname
};
210 my($exepath) = $self->{REMOTEINFO
}->{exepath
};
211 my($libpath) = $self->{REMOTEINFO
}->{libpath
};
212 my($exe) = (defined $exepath ?
213 "$exepath/" . basename
($commandline) : $commandline);
214 $commandline = "$method -l $username $remote \"";
215 if (defined $libpath) {
216 my($csh) = (defined $self->{REMOTEINFO
}->{shell
} &&
217 $self->{REMOTEINFO
}->{shell
} =~ /csh/);
218 foreach my $pvar ('DYLD_LIBRARY_PATH', 'LD_LIBRARY_PATH',
219 'LIBPATH', 'SHLIB_PATH') {
221 $commandline .= "if (! \\\$?$pvar) setenv $pvar; " .
222 "setenv $pvar $libpath:\\\$$pvar; ";
225 $commandline .= "$pvar=$libpath:\\\$$pvar; export $pvar; ";
228 my($env) = $self->{REMOTEINFO
}->{env
};
230 foreach my $pvar (keys %$env) {
232 $commandline .= "setenv $pvar $$env{$pvar}; ";
235 $commandline .= "$pvar=$$env{$pvar}; export $pvar; ";
240 $commandline .= $exe;
242 if (defined $self->{ARGUMENTS
}) {
243 $commandline .= ' '.$self->{ARGUMENTS
};
245 # Avoid modifying TAO/tests run_test.pl scripts by using the
246 # ACE_RUNTEST_ARGS environment variable to append command line
248 if ($^O
eq "nonstop_kernel") {
249 my $global_args = $ENV{'ACE_RUNTEST_ARGS'};
250 if ((length($global_args) > 0)
251 && ($commandline !~ /tao_idl/)) {
252 $commandline = $commandline
257 if (defined $self->{REMOTEINFO
}) {
259 } elsif (defined $self->{TARGET
} && defined $self->{TARGET
}->{REMOTE_SHELL
}) {
260 my($shell) = $self->{TARGET
}->{REMOTE_SHELL
};
261 my $x_env_ref = $self->{TARGET
}->{EXTRA_ENV
};
262 my($root) = $self->{TARGET
}->ACE_ROOT();
263 if (!defined $root) {
264 $root = $ENV{'ACE_ROOT'};
267 if (defined $self->{TARGET
} && defined $ENV{TEST_ROOT
} &&
268 defined $self->{TARGET
}->{TEST_ROOT
}) {
269 $exedir = PerlACE
::rebase_path
($exedir,
271 $self->{TARGET
}->{TEST_ROOT
});
272 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
273 print STDERR
"INFO: rebased run script exedir to [",$exedir,"]\n";
276 my $tgt_exedir = $exedir;
278 if (defined $self->{TARGET
} && defined $ENV{TEST_ROOT
} &&
279 defined $self->{TARGET
}->{TEST_FSROOT
}) {
280 $tgt_exedir = PerlACE
::rebase_path
($exedir,
281 $self->{TARGET
}->{TEST_ROOT
},
282 $self->{TARGET
}->{TEST_FSROOT
});
283 } elsif (defined $self->{TARGET
} &&
284 defined $self->{TARGET
}->{TARGET_FSROOT
}) {
285 $tgt_exedir = PerlACE
::rebase_path
($exedir,
286 $self->{TARGET
}->{HOST_FSROOT
},
287 $self->{TARGET
}->{TARGET_FSROOT
});
288 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
289 print STDERR
"INFO: rebased run script exedir to [",$tgt_exedir,"]\n";
292 if (!defined $self->{PIDFILE
}) {
293 # PIDFILE is based on target file system
294 $self->{PIDFILE
} = "$tgt_exedir/ace-".rand(time).".pid";
296 if (!defined $self->{SCRIPTFILE
}) {
297 # SCRIPTFILE is based on host file system
298 $self->{SCRIPTFILE
} = "$exedir/run-".rand(time).".sh";
301 my $libpath = "$root/lib";
302 if (defined $self->{TARGET
}->{LIBPATH
}) {
303 $libpath = PerlACE
::concat_path
($libpath, $self->{TARGET
}->{LIBPATH
});
305 # add working dir by default as for local executions
307 # "if [ ! -e /tmp/.acerun ]; then mkdir /tmp/.acerun; fi\n".
309 "export LD_LIBRARY_PATH=$libpath:.:\$LD_LIBRARY_PATH\n".
310 "export DYLD_LIBRARY_PATH=$libpath:.:\$DYLD_LIBRARY_PATH\n".
311 "export LIBPATH=$libpath:.:\$LIBPATH\n".
312 "export SHLIB_PATH=$libpath:.:\$SHLIB_PATH\n".
313 "export PATH=\$PATH:$root/bin:$root/lib:$libpath:.\n";
314 if (defined $self->{TARGET
}->{ace_root
}) {
316 "export ACE_ROOT=$self->{TARGET}->{ace_root}\n";
318 if (defined $self->{TARGET
}->{tao_root
}) {
320 "export TAO_ROOT=$self->{TARGET}->{tao_root}\n";
323 while ( my ($env_key, $env_value) = each(%$x_env_ref) ) {
325 "export $env_key=\"$env_value\"\n";
331 "echo \$MY_PID > ".$self->{PIDFILE
}."\n";
332 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
334 "echo INFO: Process started remote with pid [\$MY_PID]\n";
336 $run_script .= "wait \$MY_PID\n";
337 unless (open (RUN_SCRIPT
, ">".$self->{SCRIPTFILE
})) {
338 print STDERR
"ERROR: Cannot Spawn: <", $self->Executable (),
339 "> failed to create ",$self->{SCRIPTFILE
},"\n";
342 print RUN_SCRIPT
$run_script;
345 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
346 print STDERR
"INFO: created run script [",$self->{SCRIPTFILE
},"]\n", $run_script;
348 if ($self->{TARGET
}->PutFile ($self->{SCRIPTFILE
}) == -1) {
349 print STDERR
"ERROR: Failed to copy <", $self->{SCRIPTFILE
},
354 $commandline = "$shell \"source $tgt_exedir/".basename
($self->{SCRIPTFILE
})."\"";
366 # If we have -Config ARCH, do not set IGNOREEXESUBDIR, since with ARCH
367 # all executables (even those in $ACE_ROOT/bin, etc.) are located in the
368 # architecture-specific subdirectory.
369 if (@_ != 0 && !grep(($_ eq 'ARCH'), @PerlACE::ConfigList
::Configs
)) {
370 $self->{IGNOREEXESUBDIR
} = shift;
372 elsif (@_ != 0 && $self->{EXECUTABLE
} =~ /perl$/) {
373 $self->{IGNOREEXESUBDIR
} = shift;
376 return $self->{IGNOREEXESUBDIR
};
384 $self->{IGNOREHOSTROOT
} = shift;
387 return $self->{IGNOREHOSTROOT
};
390 sub RemoteInformation
395 ## Valid keys for %params
396 ## hostname - The remote hostname
397 ## method - either rsh or ssh
398 ## username - The remote user name
399 ## exepath - The remote path to the executable
400 ## shell - The shell of the remote user
401 ## libpath - A library path for libraries required by the executable
402 ## env - A hash reference of name value pairs to be set in the
403 ## environment prior to executing the executable.
405 ## At a minimum, the user must provide the remote hostname.
407 if (defined $params{'hostname'}) {
408 my(@pwd) = getpwuid($<);
409 $self->{REMOTEINFO
} = \
%params;
410 if (!defined $self->{REMOTEINFO
}->{'method'}) {
411 $self->{REMOTEINFO
}->{'method'} = 'ssh';
413 if (!defined $self->{REMOTEINFO
}->{'username'}) {
414 $self->{REMOTEINFO
}->{'username'} = $pwd[0] ||
415 $ENV{LOGNAME
} || $ENV{USERNAME
};
417 if (!defined $self->{REMOTEINFO
}->{'shell'}) {
418 $self->{REMOTEINFO
}->{'shell'} = basename
($pwd[8]);
423 ###############################################################################
425 # Spawn the process and continue;
427 sub Normalize_Executable_Name
429 my $executable = shift;
431 my $basename = basename
($executable);
432 my $dirname = dirname
($executable). '/';
434 $executable = $dirname.$PerlACE::Process
::ExeSubDir
.$basename;
443 if ($self->{RUNNING
} == 1) {
444 print STDERR
"ERROR: Cannot Spawn: <", $self->Executable (),
445 "> already running\n";
449 if (!defined $self->{EXECUTABLE
}) {
450 print STDERR
"ERROR: Cannot Spawn: No executable specified\n";
454 if ($self->{IGNOREEXESUBDIR
} == 0) {
455 if (!defined $self->{REMOTEINFO
} &&
456 !(defined $self->{TARGET
} && (defined $self->{TARGET
}->{REMOTE_SHELL
} || defined $self->{TARGET
}->{TARGET_FSROOT
})) &&
457 !-f
$self->Executable ()) {
458 print STDERR
"ERROR: Cannot Spawn: <", $self->Executable (),
467 if (defined $self->{VALGRIND_CMD
}) {
468 my $orig_cmdline = $self->CommandLine();
469 $executable = $self->{VALGRIND_CMD
};
470 my $basename = basename
($self->{EXECUTABLE
});
472 $cmdline = "$executable $orig_cmdline";
474 elsif (defined $ENV{'ACE_TEST_WINDOW'}) {
475 $cmdline = $ENV{'ACE_TEST_WINDOW'} . ' ' . $self->CommandLine();
478 $executable = $self->Executable();
479 $executable = File
::Spec
->rel2abs ($executable);
481 if (defined $self->{TARGET
} && defined $self->{TARGET
}->{TARGET_FSROOT
}) {
482 # If the target's config has a different filesystem root, rebase the executable
483 # from local root to the target's root.
484 $executable = PerlACE
::rebase_path
($executable,
485 $self->{TARGET
}->{HOST_FSROOT
},
486 $self->{TARGET
}->{TARGET_FSROOT
});
488 $cmdline = $self->CommandLine();
492 if ($self->{PROCESS
} = fork) {
496 elsif (defined $self->{PROCESS
}) {
498 my @cmdlist = $self->parse_command_line($cmdline);
499 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
500 print "INFO: $cmdline\n";
501 foreach my $arg (@cmdlist) {
502 print "INFO: argument - '$arg'\n";
505 # update environment for target
506 if (defined $self->{TARGET
}) {
507 if (!(defined $self->{TARGET
}->{REMOTE_SHELL
} || defined $self->{REMOTEINFO
})) {
508 my $x_env_ref = $self->{TARGET
}->{EXTRA_ENV
};
509 while ( my ($env_key, $env_value) = each(%$x_env_ref) ) {
510 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
511 print "INFO: adding target environment $env_key=$env_value\n";
513 $ENV{$env_key} = $env_value;
516 if ($self->{TARGET
}->{LIBPATH
}) {
517 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
518 print "INFO: adding target libpath ".$self->{TARGET
}->{LIBPATH
}."\n";
520 PerlACE
::add_lib_path
($self->{TARGET
}->{LIBPATH
});
523 if (!(defined $self->{VALGRIND_CMD
} || defined $ENV{'ACE_TEST_WINDOW'}) &&
524 (defined $self->{TARGET
}) && ($ENV{'ACE_ROOT'} ne $self->{TARGET
}->ACE_ROOT ())) {
525 my $x_dir = dirname
($executable);
529 die "ERROR: exec failed for <" . $cmdline . ">\n";
531 elsif ($! =~ /No more process/) {
532 #EAGAIN, supposedly recoverable fork error
538 print STDERR
"ERROR: Can't fork <" . $cmdline . ">: $!\n";
542 if (defined $self->{TARGET
} && defined $self->{TARGET
}->{REMOTE_SHELL
}) {
543 my $pidfile = $self->{PIDFILE
};
544 ## wait max 10 * $PerlACE::Process::WAIT_DELAY_FACTOR sec for pid file to appear
545 my $start_tm = time ();
547 if ($PerlACE::Process
::WAIT_DELAY_FACTOR
> 0) {
548 $max_wait *= $PerlACE::Process
::WAIT_DELAY_FACTOR
;
551 while ((time() - $start_tm) < $max_wait) {
552 select(undef, undef, undef, 0.2);
553 $rc = $self->ReadPidFile($pidfile);
555 $self->{REMOTE_PID
} = $rc;
559 if (!defined $self->{REMOTE_PID
}) {
560 print STDERR
"Remote command <" . $cmdline . ">: No PID found at Spawn.\n";
564 $self->{RUNNING
} = 1;
568 sub print_stacktrace_linux
572 # Get the core file pattern
573 my $core_pattern_file = "/proc/sys/kernel/core_pattern";
574 if (!(-e
$core_pattern_file)) {
575 print STDERR
"WARNING: print_stacktrace_linux: Core file pattern $core_pattern_file does not exist\n";
580 if (!open ($pattern_fh, "<", "$core_pattern_file")) {
581 print STDERR
"WARNING: print_stacktrace_linux: Could not open $core_pattern_file: $!\n";
585 my $line = <$pattern_fh>;
590 print STDERR
"WARNING: print_stacktrace_linux: Core files are handled by a separate service. Core pattern: $line\n";
594 # Find the core file from the pattern
597 if ($line =~ /^(.*)\/([^\
/]*)$/) {
605 # If /proc/sys/kernel/core_uses_pid is non-zero and the pattern
606 # doesn't have %p, then .PID is appended to the core file name.
607 my $uses_pid_file = "/proc/sys/kernel/core_uses_pid";
609 if (!open (my $uses_pid_fh, "<", "$uses_pid_file")) {
610 print STDERR
"WARNING: print_stacktrace_linux: Could not open $uses_pid_file: $!\n";
613 $line = <$uses_pid_fh>;
615 if ($line ne "" || $line ne "\n") {
618 close ($uses_pid_fh);
621 my $exec_path = $self->Executable ();
623 my $exec_name_idx = index ($pattern, "%e");
624 if ($exec_name_idx != -1) {
625 my $exec_name = File
::Basename
::basename
($exec_path);
626 # The core file name contains at most 15 characters from the executable name
627 # (https://man7.org/linux/man-pages/man5/core.5.html).
628 $exec_name = substr ($exec_name, 0, 15);
629 substr ($pattern, $exec_name_idx, 2) = $exec_name;
632 my $hname_idx = index ($pattern, "%h");
633 if ($hname_idx != -1) {
634 substr ($pattern, $hname_idx, 2) = Sys
::Hostname
::hostname
();
637 my $pid_idx = index ($pattern, "%p");
638 if ($pid_idx != -1) {
639 substr ($pattern, $pid_idx, 2) = $self->{PROCESS
};
641 elsif ($uses_pid != 0) {
642 $pattern = $pattern . "." . $self->{PROCESS
};
645 my $timestamp_idx = index ($pattern, "%t");
647 if ($timestamp_idx != -1) {
648 my $prefix = substr ($pattern, 0, $timestamp_idx);
649 my $suffix_len = length ($pattern) - $timestamp_idx - 2;
650 my $suffix = substr ($pattern, $timestamp_idx + 2, $suffix_len);
652 # Get the core file with latest timestamp.
654 if (!opendir ($dh, $path)) {
655 print STDERR
"WARNING: print_stacktrace_linux: Couldn't opendir $path: $!\n";
658 my @files = grep (/$prefix[0-9]+$suffix/, readdir ($dh));
659 my $latest_timestamp;
660 my $chosen_core_file;
661 foreach my $file (@files) {
662 my $timestamp_len = length ($file) - $timestamp_idx - $suffix_len;
663 my $timestamp = substr ($file, $timestamp_idx, $timestamp_len);
664 if (!defined $latest_timestamp) {
665 $latest_timestamp = $timestamp;
666 $chosen_core_file = $file;
668 elsif ($latest_timestamp < $timestamp) {
669 $latest_timestamp = $timestamp;
670 $chosen_core_file = $file;
674 if (defined $chosen_core_file) {
675 $core_file_path = $path . "/" . $chosen_core_file;
678 print STDERR
"WARNING: print_stacktrace_linux: Could not determine a core file with timestamp\n";
683 $core_file_path = $path . "/" . $pattern;
686 my $debugger = "gdb";
687 if ($ENV{ACE_TEST_DEBUGGER
}) {
688 $debugger = $ENV{ACE_TEST_DEBUGGER
};
690 $self->print_stacktrace_common($exec_path, $core_file_path, $debugger);
693 sub print_stacktrace_darwin
696 my $core_file_path = "/cores/core." . $self->{PROCESS
};
698 my $debugger = "lldb";
699 if ($ENV{ACE_TEST_DEBUGGER
}) {
700 $debugger = $ENV{ACE_TEST_DEBUGGER
};
702 $self->print_stacktrace_common($self->Executable (), $core_file_path, $debugger);
705 sub print_stacktrace_common
708 my $exec_path = shift;
709 my $core_file_path = shift;
710 my $preferred_db = shift;
712 if (!(-e
$core_file_path)) {
713 print STDERR
"WARNING: print_stacktrace_common: Core file $core_file_path does not exist\n";
716 if (!defined $preferred_db) {
717 $preferred_db = "gdb";
722 my $gdb_args = " $exec_path -c $core_file_path -ex bt -ex quit";
723 my $lldb_args = " $exec_path -c $core_file_path -o bt -o quit";
725 if ($preferred_db =~ /gdb/) {
726 $preferred_cmd = $preferred_db . $gdb_args;
727 $secondary_db = "lldb";
728 $secondary_cmd = $secondary_db . $lldb_args;
730 elsif ($preferred_db =~ /lldb/) {
731 $preferred_cmd = $preferred_db . $lldb_args;
732 $secondary_db = "gdb";
733 $secondary_cmd = $secondary_db . $gdb_args;
736 print STDERR
"ERROR: print_stacktrace_common: Unknown debugger ($preferred_db) requested\n";
741 if (system ("$preferred_db --version") != -1) {
742 $stack_trace = `$preferred_cmd`;
744 elsif (system ("$secondary_db --version") != -1) {
745 print STDERR
"WARNING: print_stacktrace_common: Failed printing stack trace with $preferred_db. Trying $secondary_db...\n";
746 $stack_trace = `$secondary_cmd`;
749 print STDERR
"WARNING: print_stacktrace_common: Failed printing stack trace with both $preferred_db and $secondary_db\n";
752 if (defined $stack_trace) {
753 print STDERR
"\n======= Begin stack trace of $exec_path from core file $core_file_path =======\n";
754 print STDERR
$stack_trace;
755 print STDERR
"======= End stack trace =======\n";
759 # The second argument is an optional output argument that, if present,
760 # will be passed to check_return_value function to get the signal number
761 # the process has received, if any, and/or whether there was a core dump.
768 if ($self->{RUNNING
} == 0) {
773 my %my_opts = (dump_ref
=> \
$has_core);
776 if (defined $opts->{self_crash
}) {
777 $my_opts{self_crash
} = $opts->{self_crash
};
779 if (defined $opts->{signal_ref
}) {
780 $my_opts{signal_ref
} = $opts->{signal_ref
};
784 my $status = $self->TimedWait ($timeout, \
%my_opts);
787 print STDERR
"ERROR: $self->{EXECUTABLE} timedout\n";
789 if ($ENV{ACE_TEST_LOG_STUCK_STACKS
}) {
790 my $debugger = $ENV{ACE_TEST_DEBUGGER
};
791 if (!defined $debugger) {
792 $debugger = ($^O
eq 'darwin') ?
'lldb' : 'gdb';
795 my $commands = ($debugger eq 'gdb') ?
796 "-ex 'set pagination off' -ex 'thread apply all backtrace'" : "-o 'bt all'";
797 print STDERR
"\n======= Begin stuck stacks =======\n";
798 system "$debugger --batch -p $self->{PROCESS} $commands";
799 print STDERR
"======= End stuck stacks =======\n";
802 if ($ENV{ACE_TEST_GENERATE_CORE_FILE
}) {
803 system ($^O
ne 'darwin') ?
"gcore $self->{PROCESS}"
804 : "lldb -b -p $self->{PROCESS} -o " .
805 "'process save-core core.$self->{PROCESS}'";
810 elsif ($status == 255 && $has_core && !$ENV{ACE_TEST_DISABLE_STACK_TRACE
}) {
811 if ($^O
eq 'linux') {
812 $self->print_stacktrace_linux ();
814 elsif ($^O
eq 'darwin') {
815 $self->print_stacktrace_darwin ();
819 if (defined $opts && defined $opts->{dump_ref
}) {
820 ${$opts->{dump_ref
}} = $has_core;
823 $self->{RUNNING
} = 0;
828 # Do a Spawn and immediately WaitKill
829 sub SpawnWaitKill
($;$)
835 if ($self->Spawn () == -1) {
839 return $self->WaitKill ($timeout, $opts);
842 sub TerminateWaitKill
($)
847 if ($self->{RUNNING
}) {
848 print STDERR
"INFO: $self->{EXECUTABLE} being killed.\n";
849 kill ('TERM', $self->{PROCESS
});
852 return $self->WaitKill ($timeout, {self_crash
=> 1});
855 # Really only for internal use.
856 # The second optional argument is a hash reference with the following keys.
857 # 1. "self_crash" indicates if the process may receive a signal intentionally.
858 # In that case, a signal may originate from the process, e.g., by calling abort(),
859 # or from an associated Perl script, e.g., by calling kill. If "self_crash" is
860 # missing, it has the same meaning as if "self_crash" is evaluated to false.
861 # A signal intentionally received can be either KILL, TERM, or ABRT. Any other
862 # signal indicates there was an actual error.
863 # 2. "signal_ref" is a scalar reference that will hold the signal number, if any.
864 # 3. "dump_ref" is a scalar reference that indicates if there was a core dump.
865 sub check_return_value
($)
869 my $opts = shift // {};
871 # NSK OSS has a 32-bit waitpid() status
872 my $is_NSK = ($^O
eq "nonstop_kernel");
873 my $CC_MASK = $is_NSK ?
0xffff00 : 0xff00;
875 # Exit code processing
879 elsif ($rc == $CC_MASK) {
880 print STDERR
"ERROR: <", $self->{EXECUTABLE
},
884 elsif (($rc & 0xff) == 0) {
888 # Ignore NSK 16-bit completion code
889 $rc &= 0xff if $is_NSK;
891 # Remember Core dump flag
899 # A undef means the process does not self crash
900 my $self_crash = $opts->{self_crash
};
902 # ABRT, KILL or TERM can be sent deliberately
903 if ($self_crash && ($rc == 6 || $rc == 9 || $rc == 15)) {
907 my $signal_ref = $opts->{signal_ref
};
908 if (defined $signal_ref) {
909 ${$signal_ref} = $rc;
912 my $dump_ref = $opts->{dump_ref
};
913 if (defined $dump_ref) {
914 ${$dump_ref} = $dump;
917 print STDERR
"ERROR: <", $self->{EXECUTABLE
},
920 print STDERR
"coredump from " if ($dump == 1);
922 print STDERR
"signal $rc : ", $signame[$rc], "\n";
928 sub parse_command_line
($)
932 $cmdline =~ s/^\s+//;
935 while ($cmdline ne '') {
936 if ($cmdline =~ /^\"([^\"\\]*(?:\\.[^\"\\]*)*)\"(.*)/) {
939 $unquoted =~ s/\\\"/\"/g;
940 push @cmdlist, $unquoted;
942 elsif ($cmdline =~ /^\'([^\'\\]*(?:\\.[^\'\\]*)*)\'(.*)/) {
945 $unquoted =~ s/\\\'/\'/g;
946 push @cmdlist, $unquoted;
948 elsif ($cmdline =~ /^([^\s]*)(.*)/) {
953 # this must be some kind of error
954 push @cmdlist, $cmdline;
956 $cmdline =~ s/^\s+//;
965 my $ignore_return_value = shift;
967 # If Remote PID not known, but should be
968 if (defined $self->{TARGET
} &&
969 defined $self->{TARGET
}->{REMOTE_SHELL
} &&
970 !defined $self->{REMOTE_PID
}) {
971 my $rc = $self->ReadPidFile($self->{PIDFILE
});
973 $self->{REMOTE_PID
} = $rc;
977 my $child_killed = 0;
979 if ($self->{RUNNING
} && !defined $ENV{'ACE_TEST_WINDOW'}) {
980 if (defined $self->{TARGET
} && defined $self->{TARGET
}->{REMOTE_SHELL
}) {
981 # Kill remote process
982 if (defined $self->{REMOTE_PID
}) {
983 my $cmd = $self->{TARGET
}->{REMOTE_SHELL
}." kill -s KILL ".$self->{REMOTE_PID
};
984 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
985 print STDERR
"INFO: Killing remote process <", $cmd, ">\n";
986 select(undef, undef, undef, .5);
989 # Wait to give remote process time to exit
990 select(undef, undef, undef, 3.0);
992 print STDERR
"INFO: remote process PID unknown, can't kill\n";
995 kill ('KILL', $self->{PROCESS
});
1000 for(my $i = 0; $i < 10; $i++) {
1001 my $pid = waitpid ($self->{PROCESS
}, WNOHANG
);
1003 if (! $ignore_return_value) {
1004 $self->check_return_value ($?
, {self_crash
=> 1});
1009 if (!$child_killed) {
1010 # Kill child process (may be remote shell))
1011 kill ('KILL', $self->{PROCESS
});
1014 select(undef, undef, undef, .5);
1019 $self->{RUNNING
} = 0;
1022 # Wait until a process exits.
1023 # return -1 if the process is still alive.
1027 my $timeout = shift;
1028 if (!defined $self->{PROCESS
}) {
1031 if (!defined $timeout || $timeout < 0) {
1032 return waitpid ($self->{PROCESS
}, 0);
1034 return TimedWait
($self, $timeout);
1039 # The second argument is an optional output argument that, if present,
1040 # will contain the signal number that the process has received, if any,
1041 # and/or whether there was a core dump.
1045 my $timeout = shift;
1048 if (!defined $self->{PROCESS
}) {
1052 if ($PerlACE::Process
::WAIT_DELAY_FACTOR
> 0) {
1053 $timeout *= $PerlACE::Process
::WAIT_DELAY_FACTOR
;
1056 # Multiply with 10 because we wait a tenth of a second each time
1059 while ($timeout-- != 0) {
1060 my $pid = waitpid ($self->{PROCESS
}, &WNOHANG
);
1061 if ($pid != 0 && $?
!= -1) {
1062 return $self->check_return_value ($?
, $opts);
1064 select(undef, undef, undef, 0.1);
1074 my $procmask = shift;
1078 my $valgrind_cmd = $ENV{'ACE_RUN_VALGRIND_CMD'};
1079 my $ps_cmd = 'ps -ef';
1080 my $ps_pid_field = 1;
1081 my $ps_cmd_field = 7;
1082 my $ps_skip_first = 1;
1084 if (defined $target && defined $target->{PS_CMD
}) {
1085 ## in case a special command is required
1086 ## format: <cmd>:<pid field index>:<cmd field index>[:<skip headers flag>]
1088 @ps_fields = split (/:/, $target->{PS_CMD
});
1089 $ps_cmd = $ps_fields[0];
1090 if (@ps_fields > 1) {
1091 $ps_pid_field = $ps_fields[1];
1092 if (@ps_fields > 2) {
1093 $ps_cmd_field = $ps_fields[2];
1094 if (@ps_fields > 3) {
1095 $ps_skip_first = ($ps_fields[3] == '1' ?
1 : 0);
1099 print STDERR
"ERROR: Missing field index for PID in [PS_CMD=".$target->{PS_CMD
}."]\n";
1105 if (defined $target && defined $target->{REMOTE_SHELL
}) {
1106 $which_ps = $target->{REMOTE_SHELL
} . ' which ps';
1108 $which_ps = 'which ps';
1110 my $ps_file = `$which_ps`;
1111 if (defined $target && defined $target->{REMOTE_SHELL
}) {
1112 $which_ps = $target->{REMOTE_SHELL
} . " readlink $ps_file";
1113 $ps_file = `$which_ps`;
1115 $ps_file =~ s/^\s+//;
1116 $ps_file =~ s/\s+$//;
1117 if (($ps_file =~ /busybox/) or ((-l
$ps_file) and (readlink ($ps_file)) =~ /busybox/)) {
1118 ## some embedded targets use BusyBox for base tools
1119 ## with different arguments
1125 if (defined $target && defined $target->{REMOTE_SHELL
}) {
1126 $ps_cmd = $target->{REMOTE_SHELL
}.' '.$ps_cmd;
1128 for my $line (`$ps_cmd`) {
1129 if ($ps_skip_first) {
1130 # skip first line (headers)
1134 @ps_fields = split (/\s+/, $line);
1135 if (@ps_fields > $ps_pid_field && @ps_fields > $ps_cmd_field) {
1137 $pid = $ps_fields[$ps_pid_field]; # process PID
1138 # take care of valgrind runs
1139 if (defined $valgrind_cmd) {
1140 my $pos = index ($line, $valgrind_cmd);
1142 $cmd = substr ($line, $pos + length ($valgrind_cmd));
1143 $cmd =~ s/^\s+//; # strip leading ws
1144 @ps_fields = split (/\s+/, $cmd);
1145 $cmd = $ps_fields[0];
1150 if ($ps_cmd_field >= 0) {
1151 $cmd = $ps_fields[$ps_cmd_field]; # process cmd / executable
1158 if ($cmd =~ /$procmask/) {
1159 if (defined $target && defined $target->{REMOTE_SHELL
}) {
1160 my $kill_cmd = $target->{REMOTE_SHELL
}." kill -s KILL $pid";
1161 $kill_cmd = `$kill_cmd`;
1163 kill ('KILL', $pid); # kill process
1165 if (defined $ENV{'ACE_TEST_VERBOSE'}) {
1166 print STDERR
"INFO: Killed process at [$line]\n";