perl - 如何为 Perl 制作静态分析调用图?

标签 perl static-analysis call-graph

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

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

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

最佳答案

一般情况下是做不到的:

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()" )
  • 其他我没想到的

  • 如果你想要一个比那毫无值(value)的黑客更好的解决方案,是时候开始研究 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;
    }
    

    关于perl - 如何为 Perl 制作静态分析调用图?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1295311/

    相关文章:

    perl - WWW::Mechanize::Firefox 故障排除

    c++ - 获取 llvm::Function 静态地址

    java - 在 Java 的构造函数中使用 super() 有什么问题吗?

    perl - 如何打印所有执行的子例程?

    clang - 使用 clang 为文件创建调用图

    python - 优化 shell 脚本 (bash) 以提高性能

    perl - 驼鹿测试失败

    regex - Windows 和 Linux 中的 Perl 正则表达式问题

    c - 让夹板与 win32 整数后缀一起工作

    c++调用图,但作为文本