12
votes

Comment puis-je faire un graphique d'appel d'analyse statique pour Perl?

Je travaille sur un programme PERL modérément complexe. Dans le cadre de son développement, il doit passer des modifications et des tests. En raison de certaines contraintes d'environnement, exécuter ce programme fréquemment n'est pas une option facile à faire de l'exercice.

Ce que je veux, c'est un générateur de graphique d'appel statique pour Perl. Il n'est pas nécessaire de couvrir chaque cas de bord (E, g., Redéfinir des variables pour être des fonctions ou inversement dans une évaluation).

(Oui, je sais qu'il existe une installation génératrice de graphique d'appel d'exécution avec Devel :: dprofpp, mais le temps d'exécution n'est pas garanti d'appeler toutes les fonctions. Je besoin pour pouvoir Regardez chaque fonction.)


3 commentaires

Stackoverflow.com/Questtions/1270477/...


Celui-ci demande spécifiquement analyse statique . L'autre ne précise pas s'il est statique, ou non.


Comme il n'y a pas de réponse définitive, je l'échange sur cw.


5 Réponses :


4
votes

Je ne pense pas qu'il existe un générateur de graphique d'appel "statique" pour Perl.

La prochaine chose la plus proche serait Devel :: nytprof .

L'objectif principal est que le profilage, mais que la production peut vous dire combien de fois un sous-programme a été appelé et de l'endroit où.

Si vous avez besoin de vous assurer que chaque sous-programme est appelé, vous pouvez également utiliser Devel :: Couverture , qui vérifie pour vous assurer que votre suite de test couvre chaque sous-programme.


2 commentaires

Brad, le problème est que le profilage de temps d'exécution dans le cas général ne vous donnera pas toutes les fonctions du programme. Il n'y aura qu'un sous-ensemble limité du programme exécuté dans chaque exécution. C'est pourquoi je souhaite spécifiquement un analyseur statique.


C'est pourquoi j'ai également mentionné Devel :: Couverture , il s'assure que toutes vos sous-programmes sont appelées.



8
votes

ne peut pas être fait dans le cas général:

#!/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;
}
  • appels à des fonctions qui n'utilisent pas entre parenthèses (par exemple imprimer "foo \ n"; code>) li>
  • appels à des fonctions qui sont déréférencé (par exemple $ coderef -> () code>) li>
  • les appels aux méthodes qui sont des chaînes (par exemple $ obj -> $ méthode () code>) li>
  • appelle le putt la parenthèse ouverte sur une autre ligne li>
  • d'autres choses que je n'ai pas pensé li> Ul>

    incorrectement prises p>

    • fonctions commentées (par exemple, #foo () code>) li>
    • certaines chaînes (par exemple "foo ()" code>) li>
    • d'autres choses que je n'ai pas pensé li> Ul>

      Si vous voulez une meilleure solution que bidouille sans valeur, il est temps de commencer à chercher dans PPI code> , mais même il aura des problèmes avec des choses comme $ obj -.> $ méthode () code> p> Juste parce que je me suis ennuyé, voici une version PPI code> . Il ne trouve que les appels de fonction (pas d'appels de méthode). Il fait également aucune tentative de garder les noms des sous-routines uniques (à savoir si vous appelez la même sous-programme plus d'une fois il affichera plus d'une fois). P>

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


1 commentaires

Chas - Je serais curieux de voir votre code, mais tout comme FYI, certaines tentatives existent déjà, par ex. lists.netisland.net/archives/phlpm/phlpm-2004/msg00024. HTML



4
votes

Je ne suis pas sûr qu'il soit 100% possible (car le code PERL ne peut pas être analysé statiquement en théorie, en raison de commencez par blocs et tels - voir très récent de la discussion ). De plus, les références de sous-programme peuvent rendre très difficiles à faire, même dans des endroits où commencer Les blocs n'entrent pas en jeu.

Cependant, Quelqu'un a apparemment fait la tentative - i Je ne le sais que, mais ne l'a jamais utilisé pour que l'acheteur soit méfiez-vous.


1 commentaires

Ce n'est pas 100% fonctionnel. Je suis d'accord avec ça. 90% suffit à mon avis.



2
votes

Je suis récemment tombé sur un script tout en essayant de résoudre une réponse à cette même question. Le script (lié à ci-dessous) utilise GRAPHVIZ pour créer un graphique d'appel d'un programme ou d'un module PERL. La sortie peut être dans un certain nombre de formats d'image.

http://www.teragridforum.org/mediawiki/index.php? Titre = perl_static_source_code_analysis


0 commentaires

1
votes

J'ai résolu un problème similaire récemment et souhaite partager ma solution.
Cet outil est né de désespoir de désespoir, inverse une partie non documentée d'un script hérité de 30 000 lignes, afin de mettre en œuvre une solution de bogue urgente.

Il lit le (s) code (s) source (s), utilise GraphViz pour générer un PNG, puis affiche l'image à l'écran.
Comme il utilise des plus simples regex de ligne à ligne, le formatage doit être "sain d'esprit" de sorte que la nidification puisse être déterminée.
Si le code cible est mal formaté, dirigez-le d'abord un Linter.
En outre, ne vous attendez pas à des miracles tels que l'analyse des appels de fonction dynamique.

La doublure en argent d'un simple moteur de regex est que cela peut être facilement étendu pour d'autres langues.
L'outil maintenant prend également en charge Awk, Bash, Basic, Dart, Fortran, Go, Lua, JavaScript, Kotlin, Matlab, Pascal, Perl, PHP, Python, R, Raku, Ruby, Rust, Scala, Swift et TCL.

https://github.com/koknat/callgraph


0 commentaires