2009-08-18 2 views
11

Я работаю над умеренно сложной программой Perl. В рамках своего развития он должен пройти модификации и тестирование. Из-за определенных ограничений среды выполнение этой программы часто не является вариантом, который легко осуществить.Как я могу сделать график вызовов статического анализа для Perl?

Что я хочу - это статический генератор графа вызовов для Perl. Он не должен охватывать каждый случай края (e, g., Переопределение переменных в функции или наоборот в eval).

(Да, я знаю, что существует генерация с использованием времени выполнения вызова с помощью Devel :: DprofPP, но время выполнения не гарантируется для вызова каждой функции. I необходимо, чтобы иметь возможность смотреть на каждую функцию .)

+0

http://stackoverflow.com/questions/1270477/how-can-i-generate-call-graphs-for-perl-modules-and-scripts –

+0

Это один специально просят ** Статический анализ **. Другой не указывает, является ли он статичным или нет. –

+0

Поскольку нет окончательного ответа, я заменяю это на CW. –

ответ

4

Я не думаю, что для Perl существует «статический» генератор вызовов-графов.

Следующая ближайшая вещь будет Devel::NYTProf.

Основная цель - профилирование, но вывод данных может указывать, сколько раз вызывалась подпрограмма и откуда.

Если вам нужно убедиться, что все подпрограммы вызваны, вы также можете использовать Devel::Cover, который проверяет, чтобы ваш тестовый пакет охватывал каждую подпрограмму.

+1

http://www.slideshare.net/Tim.Bunce/develnytprof-200907 –

+0

Брэд, проблема в том, что профилирование во время выполнения в общем случае не даст вам каждой функции в программе. В каждом исполнении будет только ограниченное подмножество программы. Вот почему я специально хочу статический анализатор. –

+2

Вот почему я также упомянул 'Devel :: Cover', он гарантирует, что вызовут все ваши подпрограммы. –

7

не может быть сделано в общем случае:

my $obj = Obj->new; 
my $method = some_external_source(); 

$obj->$method(); 

Однако, это должно быть довольно легко получить большое количество случаев (запустить эту программу на себе):

#!/usr/bin/perl 

use strict; 
use warnings; 

sub foo { 
    bar(); 
    baz(quux()); 
} 

sub bar { 
    baz(); 
} 

sub baz { 
    print "foo\n"; 
} 

sub quux { 
    return 5; 
} 

my %calls; 

while (<>) { 
    next unless my ($name) = /^sub (\S+)/; 
    while (<>) { 
     last if /^}/; 
     next unless my @funcs = /(\w+)\(/g; 
     push @{$calls{$name}}, @funcs; 
    } 
} 

use Data::Dumper; 
print Dumper \%calls; 

Заметим, что это не попадает

  • вызовы функций, которые не используют круглые скобки (например, print "foo\n";)
  • вызывает функции, которые разыменованы (например, $coderef->())
  • вызовы методов, которые являются строками (например $obj->$method())
  • называет Бип открытую скобку на другой линии
  • другие вещи, которые я не думал о

Это неправильно ловит

  • прокомментировал функции (например #foo())
  • некоторых строк (например, "foo()")
  • других вещей, которые я не думал о

Если вы хотите лучшее решение, чем тот бесполезный хак, настало время, чтобы начать поиск в PPI, но даже это будет иметь проблемы с вещами, как $obj->$method().

Просто потому, что мне было скучно, вот версия, которая использует PPI. Он находит только вызовы функций (а не вызовы методов). Он также не пытается сохранить имена подпрограмм уникальными (т.если вы вызываете одну и ту же подпрограмму более одного раза, она будет отображаться более одного раза).

#!/usr/bin/perl 

use strict; 
use warnings; 

use PPI; 
use Data::Dumper; 
use Scalar::Util qw/blessed/; 

sub is { 
    my ($obj, $class) = @_; 
    return blessed $obj and $obj->isa($class); 
} 

my $program = PPI::Document->new(shift); 

my $subs = $program->find(
    sub { $_[1]->isa('PPI::Statement::Sub') and $_[1]->name } 
); 

die "no subroutines declared?" unless $subs; 

for my $sub (@$subs) { 
    print $sub->name, "\n"; 
    next unless my $function_calls = $sub->find(
     sub { 
      $_[1]->isa('PPI::Statement')    and 
      $_[1]->child(0)->isa("PPI::Token::Word") and 
      not (
       $_[1]->isa("PPI::Statement::Scheduled") or 
       $_[1]->isa("PPI::Statement::Package") or 
       $_[1]->isa("PPI::Statement::Include") or 
       $_[1]->isa("PPI::Statement::Sub")  or 
       $_[1]->isa("PPI::Statement::Variable") or 
       $_[1]->isa("PPI::Statement::Compound") or 
       $_[1]->isa("PPI::Statement::Break")  or 
       $_[1]->isa("PPI::Statement::Given")  or 
       $_[1]->isa("PPI::Statement::When") 
      ) 
     } 
    ); 
    print map { "\t" . $_->child(0)->content . "\n" } @$function_calls; 
} 
+1

Chas - Мне было бы интересно увидеть ваш код, но так же, как FYI, некоторые попытки уже существуют, например. http://lists.netisland.net/archives/phlpm/phlpm-2004/msg00024.html – DVK

4

Я не уверен, что это 100% возможно (с код Perl не может быть статически проанализированы в теории, из-за BEGIN блоков и таких - см very recent SO discussion). Кроме того, ссылки на подпрограммы могут затруднить выполнение даже в тех местах, где блоки BEGIN не вступают в игру.

Однако, someone apparently made the attempt - Я знаю об этом, но никогда не использовал его, поэтому остерегайтесь покупателя.

+0

Это не работает на 100%. Я в порядке с этим. На мой взгляд, достаточно 90%. –

2

Я недавно наткнулся на скрипт, пытаясь найти ответ на этот же вопрос. Скрипт (связанный ниже) использует GraphViz для создания графика вызовов программы или модуля Perl. Вывод может быть в нескольких форматах изображений.

http://www.teragridforum.org/mediawiki/index.php?title=Perl_Static_Source_Code_Analysis

Смежные вопросы