12

私は適度に複雑な Perl プログラムに取り組んでいます。開発の一環として、変更とテストを行う必要があります。特定の環境の制約により、このプログラムを頻繁に実行することは、簡単に実行できるオプションではありません。

私が欲しいのは、Perl 用の静的コールグラフ ジェネレーターです。すべてのエッジ ケースをカバーする必要はありません (たとえば、変数を関数に再定義する、または eval でその逆を行う)。

(はい、Devel::DprofPP にはランタイム コールグラフ生成機能があることは知っていますが、ランタイムがすべての関数を呼び出すことが保証されているわけではありません。各関数を確認できる必要があります。)

4

5 に答える 5

8

一般的なケースでは実行できません:

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;
}
于 2009-08-18T18:29:38.250 に答える
4

Perl用の「静的な」コールグラフジェネレーターはないと思います。

次に近いのはですDevel::NYTProf

主な目標はプロファイリングですが、その出力から、サブルーチンが呼び出された回数と場所がわかります。

すべてのサブルーチンが呼び出されることを確認する必要がある場合は、を使用Devel::Coverして、テストスイートがすべてのサブルーチンをカバーしていることを確認することもできます。

于 2009-08-18T17:57:21.153 に答える
4

BEGINそれが 100% 実現可能かどうかはわかりません (Perl コードは理論的にはブロックなどのために静的に分析できないため、ごく最近の SO の議論を参照してください)。BEGINさらに、サブルーチン参照により、ブロックが機能しない場所でも実行が非常に困難になる場合があります。

しかし、誰かが試みたようです - 私はそれを知っているだけで、使用したことがないので、バイヤーは注意してください.

于 2009-08-18T18:29:55.317 に答える
2

私は最近、この同じ質問に対する答えを見つけようとしているときにスクリプトに出くわしました。スクリプト (以下にリンク) は、GraphViz を使用して Perl プログラムまたはモジュールのコール グラフを作成します。出力は、さまざまな画像形式にすることができます。

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

于 2012-10-19T21:13:11.237 に答える