如何为Perl创建静态分析调用图?

Pau*_*han 12 perl static-analysis call-graph

我正在开发一个中等复杂的Perl程序.作为其开发的一部分,它必须经过修改和测试.由于某些环境限制,经常运行此程序不是一个易于操作的选项.

我想要的是Perl的静态调用图生成器.它不必涵盖每个边缘情况(例如,在eval中将变量重新定义为函数或反之亦然).

(是的,我知道有一个运行时调用图生成工具与Devel :: DprofPP,但运行时不能保证调用每个函数.我需要能够查看每个函数.)

Cha*_*ens 8

在一般情况下无法完成:

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

$obj->$method();
Run Code Online (Sandbox Code Playgroud)

但是,获取大量案例应该相当容易(对自己运行此程序):

#!/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;
Run Code Online (Sandbox Code Playgroud)

注意,这个错过了

  • 调用不使用括号的函数(例如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;
}
Run Code Online (Sandbox Code Playgroud)


Bra*_*ert 4

我认为 Perl 没有“静态”调用图生成器。

下一个最接近的事情是Devel::NYTProf

主要目标是进行分析,但它的输出可以告诉您子例程被调用的次数以及从何处调用。

如果您需要确保调用每个子例程,您还可以使用Devel::Cover,它会检查以确保您的测试套件涵盖每个子例程。

  • 这就是为什么我还提到了“Devel::Cover”,它确保所有子例程都被调用。 (2认同)