Как я могу сделать график вызовов статического анализа для Perl?

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

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

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

Ответ 1

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

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())
  • вызывает putt открытую круглую скобку на другой строке
  • другие вещи, о которых я не думал.

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

  • прокомментированные функции (например, #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;
}

Ответ 2

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

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

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

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

Ответ 3

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

Однако кто-то явно сделал попытку - я знаю только об этом, но никогда не использовал его, чтобы остерегаться покупателя.

Ответ 4

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

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