代码之家  ›  专栏  ›  技术社区  ›  Sled bayer

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

  •  4
  • Sled bayer  · 技术社区  · 15 年前

    我有一个Perl脚本(在VisualBox中运行XunBuntu-LuxLyx),它封装了几个C/C++二进制文件,其中一个输入了另一个。其中一条线一般包括:

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

    对于某些输入文件,代码会导致coredump,但两者都会 $ret_val $ret_code 分别为0和“”。当我运行它时,我可以看到错误滚动,但是我似乎没有办法用编程的方式“捕获”它。 我怎么能这样做? 其目的是出错时从输入中删除一些行,然后重试解析。

    以下是错误:

    *** 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`;
    
    4 回复  |  直到 15 年前
        1
  •  0
  •   Hasturkun    15 年前

    首先,你有一个无用的 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 从而有效地实现自动化

        2
  •  1
  •   daxim Fayland Lam    15 年前

    首先,在你展示的代码中有一些可疑的东西:你得到了 $? 在实际运行命令之前。现在我来讨论一下你想写什么:

    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 ... . 根据shell的不同,这可能是管道中最后一个命令的状态,即, code/parser (ksh,zsh),或始终为0(大多数壳,包括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 = $?;
    

    如果你有一个有用的命令而不是 你最好的选择是完全不使用外壳。这还有其他一些小好处:少了一个要掌握的工具;更容易移植到非UNIX系统;使用包含shell元字符的文件名(这也可以通过系统地使用 quotemeta )这是这个想法的要点(未经检验); perldoc -f open perldoc 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 $!;
    }
    
        3
  •  0
  •   Ana Betts    15 年前

    因为CRT正在中止程序(也就是说,它实际上没有 崩溃 通过一个信号,CRT看到了垃圾堆金丝雀并手动中止了进程),其返回值将为零。我认为你在这里能做的最好的事情是:

    `cat $input | c_binary 2>&1`
    

    这样就可以捕获crt-gunk,并在perl脚本中检测到它。

        4
  •  0
  •   Greg Bacon    15 年前

    为您的 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
    
    *** stack smashing detected ***: ./c_binary terminated
    ./prog.pl: c_binary exited 0
      - terminated by signal 11

    如你所见,你需要使用 WIFSIGNALED WTERMSIG POSIX 模块以编程方式检测 C2二进制 被一个信号杀死,而不仅仅是出口状态本身:

    无线电信号的

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

    韦特米格

    WTERMSIG($?) 返回子进程终止的信号(仅当 WiFielDead($?) 是真的)