Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/dart/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何为Perl生成静态分析调用图?_Perl_Static Analysis_Call Graph - Fatal编程技术网

如何为Perl生成静态分析调用图?

如何为Perl生成静态分析调用图?,perl,static-analysis,call-graph,Perl,Static Analysis,Call Graph,我正在开发一个中等复杂的Perl程序。作为其开发的一部分,它必须经过修改和测试。由于某些环境限制,经常运行此程序不是一个容易练习的选项 我想要的是一个用于Perl的静态调用图生成器。它不必覆盖每一种边缘情况(例如,在eval中将变量重新定义为函数,反之亦然) (是的,我知道有一个使用Devel::dprofp的运行时调用图生成工具,但运行时不能保证调用每个函数。我需要能够查看每个函数。)我认为Perl没有“静态”调用图生成器 下一个最接近的事情是 主要目标是分析,但它的输出可以告诉您子例程被调用

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

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

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

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

下一个最接近的事情是

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


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

在一般情况下无法完成:

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

$obj->$method();
然而,获得大量案例应该相当容易(针对自身运行此程序):


我不确定它是否100%可行(因为Perl代码在理论上无法静态分析,这是由于
BEGIN
块之类的原因-请参阅)。此外,即使在
BEGIN
块不起作用的地方,子例程引用也可能使操作变得非常困难


然而,-我只知道它,但从未使用过它,所以买家要小心。

我最近在试图解决同一个问题时,偶然发现了一个脚本。该脚本(链接到下面)使用GraphViz创建Perl程序或模块的调用图。输出可以是多种图像格式


我最近解决了一个类似的问题,希望与大家分享我的解决方案。
这个工具是出于绝望而诞生的,它解开了一个30000行遗留脚本中未记录的部分,以便实现一个紧急的bug修复

它读取源代码,使用GraphViz生成png,然后在屏幕上显示图像。
由于它使用简单的逐行正则表达式,因此格式必须“合理”,以便可以确定嵌套。
如果目标代码状态不好,请先通过perltidy运行它。
另外,不要期望奇迹出现,比如解析动态函数调用、开始块等等

简单的正则表达式引擎的好处在于,它可以轻松地扩展到其他语言。
该工具现在还支持awk、bash、basic、dart、fortran、go、lua、javascript、kotlin、matlab、pascal、perl、php、python、r、raku、ruby、rust、scala、swift和tcl


Chas-我很想看看您的代码,但仅供参考,一些尝试已经存在,例如,它不是100%可行的。我同意。在我看来,90%就足够了。Brad,问题是在一般情况下运行时评测并不能提供程序中的所有功能。在每次执行中只会运行有限的程序子集。这就是为什么我特别想要一个静态分析器。这就是为什么我还提到了
Devel::Cover
,它确保所有的子例程都被调用。这一个特别要求进行静态分析。另一个没有说明它是否是静态的。因为没有确定的答案,我把它换成CW。这是很酷的东西,克里斯!
#!/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;
#!/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;
}