2009-08-18 14 views
11

Pracuję nad umiarkowanie złożonym programem Perla. W ramach swojego rozwoju musi przejść przez modyfikacje i testy. Ze względu na pewne ograniczenia środowiskowe często uruchamianie tego programu nie jest opcją łatwą do wykonania.Jak utworzyć wykres wywołań statycznych dla Perla?

Potrzebuję statycznego generatora wywołań dla Perla. Nie musi obejmować przypadku każdej krawędzi (e, g., Przedefiniowania zmiennych jako funkcji lub odwrotnie w eval).

(Tak, wiem, że istnieje funkcja generowania wywołań w czasie rzeczywistym z Devel :: DprofPP, ale nie jest gwarantowane uruchamianie każdej funkcji.I potrzebujesz, aby móc przeglądać każdą funkcję .)

+0

http://stackoverflow.com/questions/1270477/how-can-i-generate-call-graphs-for-perl-modules-and-scripts –

+0

Ten jest specjalnie prośbą o ** Analiza statyczna **. Drugi nie określa, czy jest statyczny, czy nie. –

+0

Ponieważ nie ma jednoznacznej odpowiedzi, zamieniam to na CW. –

Odpowiedz

4

Nie sądzę, że istnieje "statyczny" generator wywołań dla Perla.

Najbliższa rzecz to Devel::NYTProf.

Głównym celem jest profilowanie, ale jego wynik może informować, ile razy podprogram został wywołany i skąd.

Jeśli chcesz się upewnić, że każdy podprogram jest wywoływany, możesz również użyć Devel::Cover, który sprawdza, czy twój zestaw testów obejmuje każdy podprogram.

+1

http://www.slideshare.net/Tim.Bunce/develnytprof-200907 –

+0

Problem polega na tym, że profilowanie w czasie wykonywania w ogólnym przypadku nie daje wszystkich funkcji w programie. W każdym wykonaniu będzie tylko ograniczony podzbiór programu. Właśnie dlatego chcę statycznego analizatora. –

+2

Dlatego też wspomniałem o 'Devel :: Cover', upewniałem się, że wszystkie twoje podprogramy są wywoływane. –

7

nie można zrobić w ogólnym przypadku:

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

$obj->$method(); 

Należy jednak być dość łatwo dostać dużą liczbę przypadków (Uruchom ten program przeciwko sobie):

#!/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; 

Uwaga, ten strzela

  • wywołań funkcji, które nie używają nawiasów (np print "foo\n";)
  • połączenia z funkcjami, które zostały dereferencjonowane (np. $coderef->())
  • wywołania metod, które są ciągi (np $obj->$method())
  • wzywa putt otwarty nawias na innej linii
  • inne rzeczy, nie myślałem o

on nieprawidłowo łapie

  • skomentował funkcje (np. #foo())
  • niektóre struny (np. "foo()")
  • inne rzeczy, nie myślałem o

Jeśli chcesz lepszym rozwiązaniem niż to bezwartościowy siekać, nadszedł czas, aby zacząć szukać w PPI, ale nawet będzie miał problemy z rzeczy jak $obj->$method().

Tylko dlatego, że się nudziłem, tutaj jest wersja, która używa PPI. Wyszukuje tylko wywołania funkcji (nie wywołania metod). Nie podejmuje również próby uniknięcia nazw podprogramów (tj.jeśli wywołasz ten sam podprogram więcej niż raz, pojawi się on więcej niż raz).

#!/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; 
} 
+1

Chas - Chciałbym zobaczyć twój kod, ale tak jak FYI, niektóre próby już istnieją, np. http://lists.netisland.net/archives/phlpm/phlpm-2004/msg00024.html – DVK

4

nie jestem pewien, że jest w 100% możliwe (ponieważ kod Perl nie można statycznie analizowane w teorii, ze względu na BEGIN bloki i takie - patrz very recent SO discussion). Ponadto podprogramy mogą utrudniać pracę nawet w miejscach, gdzie bloki BEGIN nie wchodzą w grę.

Jednak, someone apparently made the attempt - Wiem tylko o tym, ale nigdy go nie użyłem, więc strzeż się kupującego.

+0

To nie jest w stu procentach wykonalne. Nie przeszkadza mi to. 90% jest moim zdaniem wystarczające. –

Powiązane problemy