Perl系统调用导致核心转储,但$? 保持零

我有一个Perl脚本(在VirtualBox内的Xubuntu Lucid Lynx上运行),它包装了几个C / C ++二进制文件,将其中一个的input提供给其他人。 其中一条线一般包括:

my $ret_code=`cat $input | c_binary`; my $ret_val= $?; 

对于一些input文件,代码会导致coredump,但$ret_val$ret_code分别为0和“”。 当我运行它时,我可以看到滚动的错误,但是我似乎没有办法“编程”这个程序。 我怎么能这样做? 意图是错误地从input中删除一些行,然后重试parsing。

这里是错误:

 *** stack smashing detected ***: code/parser terminated ======= Backtrace: ========= /lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x50)[0x798390] /lib/tls/i686/cmov/libc.so.6(+0xe233a)[0x79833a] code/parser[0x804edd8] [0x2e303039] ======= Memory map: ======== 0043b000-0043c000 r-xp 00000000 00:00 0 [vdso] 0045a000-00475000 r-xp 00000000 08:01 11041 /lib/ld-2.11.1.so 00475000-00476000 r--p 0001a000 08:01 11041 /lib/ld-2.11.1.so 00476000-00477000 rw-p 0001b000 08:01 11041 /lib/ld-2.11.1.so 006b6000-00809000 r-xp 00000000 08:01 10897 /lib/tls/i686/cmov/libc-2.11.1.so 00809000-0080a000 ---p 00153000 08:01 10897 /lib/tls/i686/cmov/libc-2.11.1.so 0080a000-0080c000 r--p 00153000 08:01 10897 /lib/tls/i686/cmov/libc-2.11.1.so 0080c000-0080d000 rw-p 00155000 08:01 10897 /lib/tls/i686/cmov/libc-2.11.1.so 0080d000-00810000 rw-p 00000000 00:00 0 008ba000-008d7000 r-xp 00000000 08:01 8268 /lib/libgcc_s.so.1 008d7000-008d8000 r--p 0001c000 08:01 8268 /lib/libgcc_s.so.1 008d8000-008d9000 rw-p 0001d000 08:01 8268 /lib/libgcc_s.so.1 00c89000-00cad000 r-xp 00000000 08:01 10901 /lib/tls/i686/cmov/libm-2.11.1.so 00cad000-00cae000 r--p 00023000 08:01 10901 /lib/tls/i686/cmov/libm-2.11.1.so 00cae000-00caf000 rw-p 00024000 08:01 10901 /lib/tls/i686/cmov/libm-2.11.1.so 08048000-08055000 r-xp 00000000 08:01 407893 /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser 08055000-08056000 r--p 0000c000 08:01 407893 /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser 08056000-08057000 rw-p 0000d000 08:01 407893 /home/abugorsk/Documents/code/stepbystep/collins-parser/code/parser 08057000-0c50f000 rw-p 00000000 00:00 0 0e168000-0fa57000 rw-p 00000000 00:00 0 [heap] b44a3000-b77c9000 rw-p 00000000 00:00 0 b77da000-b77dc000 rw-p 00000000 00:00 0 bff2b000-bff40000 rw-p 00000000 00:00 0 [stack] Aborted 

返回的值是:

 LOG: Parser return code: 0 LOG: Parser return value: 

有问题的实际代码片段:

 my $command = "cd $STEPBYSTEP_HOME/collins-parser; cat models/model$model_num/events | code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log"; llog "Executing command: $command"; my $ret_code = $?; llog "Parser return code: $ret_code"; my $ret_val = `$command`; 

首先,在命令行中有一个无用的cat ,可以很容易地被重定向所取代。

我会尝试改变命令,如下所示

 my $command = "cd $STEPBYSTEP_HOME/collins-parser && code/parser $src models/model$model_num/grammar 10000 1 1 1 1 < models/model$model_num/events 1> $dest 2> $parse_log"; 

另外,如果你想尽量减少你的输入文件找到导致崩溃的东西,我强烈建议使用Delta ,这可以有效地自动化

首先,你显示的代码中有些东西是可疑的:你得到的价值是$? 在实际运行命令之前。 现在我将讨论我认为你想写的东西:

 my $command = "cd $STEPBYSTEP_HOME/collins-parser;" . "cat models/model$model_num/events | code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log"; my $ret_val = `$command`; my $ret_code = $?; 

之后, $ret_code包含整个shell命令的状态。 这又是列表中最后一个命令的状态,这是一个管道cat ... | code/parser ... cat ... | code/parser ... 取决于shell,这可能是管道中最后一个命令的状态,即code/parser (ksh,zsh),或者始终为0(大部分shell,包括ash,bash和pdksh)。

在你的情况下,有一个简单的修复,这是摆脱cat的无用的使用:

 my $command = "cd $STEPBYSTEP_HOME/collins-parser &&" . "<models/model$model_num/events code/parser $src models/model$model_num/grammar 10000 1 1 1 1 1> $dest 2> $parse_log"; my $ret_val = `$command`; my $ret_code = $?; 

如果你有一个有用的命令,而不是cat ,你最好的选择将是完全免费的壳。 这也有其他一些小的好处:一个少工具要掌握; 更容易移植到非unix系统; 与包含shell元字符的文件名一起工作(这也可以通过系统地使用quotemeta来实现)。 这是这个想法的主旨(未经测试); perldoc -f openperldoc perlipc可能会有帮助。

 use File::Slurp; if (open my $fh, "|-") { # Parent code my $ret_val = read_file($fh); close($ret_code); my $ret_code = $?; ... } else { # Child code chdir "$ENV{STEPBYSTEP_HOME}/collins-parser" or die $!; open STDIN, "<", "models/model$model_num/events" or die $!; open STDOUT, ">", $dest or die $!; open STDERR, ">", $parse_log or die $!; exec "code/parser", $src, "models/model$model_num/grammar", "1", "1", "1", "1", "1"; die $!; } 

由于CRT正在中止程序(即,它实际上并没有通过信号发生崩溃 ,所以CRT看到了堆栈的金丝雀,手动中止了这个过程),它的返回值将为零。 我认为你可以在这里做的最好的是:

 `cat $input | c_binary 2>&1` 

这样CRT gunk将被捕获,你可以在Perl脚本中检测到它。

编译这个简单的替身你的c_binary

 #include <string.h> void f(void) { char smallbuf[9]; strcpy(smallbuf, "dy-no-MITE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); } int main(void) { f(); return 0; } 

和这个Perl程序来运行它的图像

 #! /usr/bin/perl use warnings; use strict; use POSIX; if (system("./c_binary") == 0) { print "$0: c_binary exited normally\n"; } else { warn "$0: c_binary exited ", ($? >> 8), "\n", WIFSIGNALED($?) ? (" - terminated by signal ", WTERMSIG($?), "\n") : (); } 

我明白了

  $ ./boom

 ***堆栈粉碎检测到***:./c_binary终止
 ./prog.pl:c_binary已退出0
   - 由信号11终止 

正如你所看到的,你需要使用POSIX模块中的WIFSIGNALEDWTERMSIG来以编程的方式检测c_binary是否被一个信号杀死了,而不仅仅是退出状态本身:

WIFSIGNALED

WIFSIGNALED($?)如果由于信号而终止子进程,则返回true

WTERMSIG

WTERMSIG($?)返回子进程终止的信号(仅当WIFSIGNALED($?)为真时才有意义)