wiki:InsiaProgPerlDebug

Sommaire

Programmation Perl - Le debugger

1. Présentation

Le debugger fait partie intégrante de Perl et est toujours distribué avec l'interpréteur. Il est implémenté majoritairement sous la forme de modules Perl (appelés DB::..., sans rapport avec les bases de données!). Le debugger est également le socle du profileur (pour étudier les performances d'un programme et les optimiser).

Sans le debugger

Nous avons vu qu'il était facile d'utiliser la console et quelques print bien placés pour obtenir des traces simples et facile à lire, par ex:

print "Liste 'bla': ".join(' - ', @bla)."\n";
print "$_ => $tab{$_]\n" foreach keys %tab;

Mais ces affichages sont limités, rébarbatifs pour les tableaux, et surtout ne peuvent pas rendre compte facilement de structures imbriquées basées sur les références. On peut alors appeler à la rescousse un module standard très pratique:

use Data::Dumper;

print Dumper(\@list, \%tab);

Ceci affichant par exemple:

$VAR1 = [
          'lundi',
          'mardi',
          'mercredi'
        ];
$VAR2 = {
          'aout' => 'chaud',
          'mai' => 'bon',
          'janvier' => 'froid'
        };

Notez l'utilisation des références sur des listes ou des tableaux lors de l'appel à Dumper: sans passage par référence, les structures sont mises à plat sous forme de liste et Dumper ne peut que les considérer comme des variables distinctes à afficher. Ainsi avec print Dumper(@list) on obtiendrai:

$VAR1 = 'lundi';
$VAR2 = 'mardi';
$VAR3 = 'mercredi';

Il reste que ces méthodes d'introspection sont limitées et demandent des modifications fréquentes du code à analyser.

2. Utilisation

Il suffit d'invoquer son programme avec l'option -d de l'interpréteur:

$ perl -d ./server.pl

Loading DB routines from perl5db.pl version 1.28
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(./server.pl:14): my $port = 2500;
  DB<1>

Afin de préserver votre contexte de debug (points d'arrêts, sondes, etc), vous pouvez "recharger" le debugger avec la commande R quand vous avez modifié votre programme et que vous voulez continuer à le debugger. Il est donc fortement conseillé d'utiliser en parallèle son éditeur de texte et sa session de debug.

Note: le debugger utilise la complétion partout où cela est possible (en particulier sur les noms de variables et de fonctions), n'oubliez pas d'en abuser !

Flux du programme

Le programme est normalement compilé mais son exécution reste en attente. Pour piloter l'exécution on a accès aux commandes suivantes:

Commande Nom Action
s Single step instruction suivante, avec entrée dans les fonctions
n Next instruction suivante, sans entrée dans les fonctions
r Return continue jusqu'à la fin de la fonction courante
c (line/sub) Continue reprend l'exécution, optionnellement jusqu'à une ligne ou l'entrée d'une fonction

Points d'arrêts

Le debugger devient nettement plus intéressant quand il s'agit de programmer des interruptions à des endroits choisis de son programme:

Commande Nom Action
b (ligne/sub (cond)) Breakpoint Point d'arrêt, ligne courante ou spécifiée ou fonction, avec condition optionnelle
B ligne Delete b Effacer le point d'arrêt d'une ligne donnée
L List Liste les points d'arrêts (et sondes+actions)

L'utilisation des points d'arrêt mérite quelques exemples. On remarque notamment que les conditions sont des expressions Perl normales (sans le if qui est sous entendu):

  DB<2> b receive_from_client
  DB<3> L
./server.pl:
 73:      my ($cli) = @_;
   break if (1)

  DB<3> b send_to_client @_[0]->{'name'} eq 'mickey'
  DB<4> L
./server.pl:
 65:      my ($cli, $msg) = @_;
   break if (@_[0]->{'name'} eq 'mickey')

Note: ces exemples sont basés sur le programme source:/insia/perl/bomberman/server.pl

Contexte d'exécution

Suivant les instructions exécutées, le debugger met à jour son contexte en se déplaçant dans le programme (dernier point d'arrêt positionné, etc). On peut obtenir des informations sur le contexte du code en train d'être analysé ou qui va être exécuté de plusieurs manières:

Commande Nom Action
l (ligne) line affiche la ligne du contexte courant et quelques suivantes
v (ligne) view affiche les lignes "autour" du contexte courant
. home positionne le contexte courant sur la prochaine ligne à exécuter

On peut également à tout moment obtenir la suite d'appel de fonctions (backtrace) qui nous a amené dans notre contexte courant:

  DB<2> T
. = main::send_to_client(ref(HASH), 'JOIN-STATUS OK Welcome...') called from file `./server.pl' line 142
. = main::op_join(ref(HASH), 'noname') called from file `./server.pl' line 126
. = main::op_client(ref(HASH)) called from file `./server.pl' line 281

Introspection

Ou le nom savant pour décrire la possibilité de recenser les paquets, les fonctions et les variables déclarées.

Commande Nom Action
p ... print l'instruction print de Perl, avec les mêmes limitations
x ... examine affichage automatique comparable à Data::Dumper
S regex subs affiche les fonctions, recherche par expression régulière

La fonction la plus utilise sera sans conteste x (voir les watches plus loin toutefois), et suggère les mêmes techniques que Data::Dumper concernant les références:

  DB<5> x %linfo
  empty array
  DB<6> x \%linfo
0  HASH(0x8250214)
     empty hash

Les noms des fonctions sont toujours préfixées par leur paquet d'origine, ceci permettant d'analyser n'importe quel emplacement d'un programme modulaire. Le programme principal est représenté par le paquet main en Perl:

  DB<7> S main::
main::BEGIN
main::bomb_explode
main::check_bomb_timeout
main::del_client
main::dumpValue
main::dumpvar
main::is_bomb_at
main::is_other_player_at
[...]

Surveillance

Pour faire l'analyse d'un programme selon une variable et ses changements d'état, positionner correctement les points d'arrêts et effectuer les affichagent qui conviennent est peu efficace. Perl peut surveiller pour nous tout changement d'état, et en particulier des conditions quelconques sur une variable quel que soit l'endroit où celle-ci est modifiée: c'est ce qu'on appelle les sondes (watches en anglais).

Commande Nom Action
w variable|expression watch pose une sonde: arrête le programme si la variable est modifiée ou l'expression est vérifiée
W ... delete watch supprime une sonde

La commande L permet d'afficher les sondes posées (cf. flux du programme plus haut). Exemple d'utilisation:

  DB<30> w @clients
  DB<31> w @clients > 1

Watchpoint 0:   @clients changed:
    old value:  ''
    new value:  'HASH(0x85eaf6c)'

3. Le profileur

L'outil de "profilage" de Perl est basé sur les mécanisme de debug (principes d'interception de code similaire) et permet d'obtenir un aperçu précis de la répartion du temps dans les différents appels de fonction. Il suffit de lancer son programme avec l'option ad hoc, et on obtient en fin d'exécution un fichier de mesures nommé tmon.out. On peut analyser ce fichier avec le programme standard dprofpp:

$ perl -d:DProf ./client.pl
[...]

$ dprofpp tmon.out 
Total Elapsed Time = 19.32377 Seconds
  User+System Time = 6.613772 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c  Name
 97.2   6.429  6.429    870   0.0074 0.0074  SDL::BlitSurface
 0.76   0.050  0.050      1   0.0500 0.0500  SDL::Init
 0.74   0.049  0.049    435   0.0001 0.0001  SDL::UpdateRects
 0.44   0.029  0.029    435   0.0001 0.0001  SDL::Delay
 0.29   0.019  0.068    435   0.0000 0.0002  SDL::Surface::update
 0.15   0.010  0.010      2   0.0050 0.0050  SDL::IMGLoad
 0.15   0.010  0.010      3   0.0033 0.0033  DynaLoader::dl_load_file
 0.15   0.010  0.010      5   0.0020 0.0020  SDL::Event::BEGIN
 0.15   0.010  0.010     13   0.0008 0.0008  Exporter::export
 0.15   0.010  0.010      7   0.0014 0.0014  IO::Handle::BEGIN
 0.15   0.010  0.020      6   0.0017 0.0033  IO::Socket::BEGIN
 0.14   0.009  0.009    435   0.0000 0.0000  SDL::PollEvent
 0.14   0.009  0.038    435   0.0000 0.0001  SDL::App::delay
 0.11   0.007  6.436    870   0.0000 0.0074  SDL::Surface::blit
 0.08   0.005  6.544    435   0.0000 0.0150  main::win_update
Last modified 13 years ago Last modified on May 12, 2007, 9:41:31 PM