Системный вызов Perl вызывает дамп ядра, но $? остается нулевым
У меня есть Perl-скрипт (работающий на Xubuntu Lucid Lynx в VirtualBox), который охватывает несколько двоичных файлов 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 ответа
Во-первых, у вас есть бесполезный 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
содержит статус всей команды оболочки. Это, в свою очередь, состояние последней команды в списке, которая является конвейером cat ... | code/parser ...
, В зависимости от оболочки это может быть статус последней команды в конвейере, т. Е. 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 = $?;
Если у вас была полезная команда вместо cat
, ваш лучший вариант будет вообще обойтись без оболочки. Это также имеет другие незначительные преимущества: на один инструмент меньше освоить; проще переносить на не-unix системы; работает с именами файлов, содержащими метасимволы оболочки (это также может быть достигнуто путем систематического использования 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 $!;
}
Поскольку CRT прерывает программу (т. Е. Фактически не происходит сбой по сигналу, CRT увидел канарейку стека и вручную прервал процесс), его возвращаемое значение будет равно нулю. Я думаю, что лучшее, что вы можете сделать здесь:
`cat $input | c_binary 2>&1`
так, чтобы CRT-ганк был захвачен, и вы можете обнаружить его в скрипте 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
Итак, как вы можете видеть, вам нужно использовать WIFSIGNALED
а также WTERMSIG
от POSIX
модуль для программного обнаружения c_binary
был убит сигналом, а не только состоянием выхода:
WIFSIGNALED
WIFSIGNALED($?)
возвращает true, если дочерний процесс завершился из-за сигналаWTERMSIG
WTERMSIG($?)
возвращает сигнал, для которого дочерний процесс завершен (имеет смысл, еслиWIFSIGNALED($?)
правда)