Outils pour utilisateurs

Outils du site


perl:le_debugger

Ceci est une ancienne révision du document !


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 :

{| bgcolor=“#f7f8ff” cellpadding=“3” cellspacing=“0” border=“1” style=“font-size: 95%; border: gray solid 1px; border-collapse: collapse;”

'Commande' 'Nom'
's' Single step
'n' Next
'r' Return
'c' (line/sub) Continue

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 :

{| bgcolor=“#f7f8ff” cellpadding=“3” cellspacing=“0” border=“1” style=“font-size: 95%; border: gray solid 1px; border-collapse: collapse;”

'Commande' 'Nom'
'b' (ligne/sub (cond)) Breakpoint
'B' ligne Delete b
'L' List

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 :

{| bgcolor=“#f7f8ff” cellpadding=“3” cellspacing=“0” border=“1” style=“font-size: 95%; border: gray solid 1px; border-collapse: collapse;”

'Commande' 'Nom'
'l' (ligne) line
'v' (ligne) view
'.' home

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.

{| bgcolor=“#f7f8ff” cellpadding=“3” cellspacing=“0” border=“1” style=“font-size: 95%; border: gray solid 1px; border-collapse: collapse;”

'Commande' 'Nom'
'p' … print
'x' … examine
'S' regex subs

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).

{| bgcolor=“#f7f8ff” cellpadding=“3” cellspacing=“0” border=“1” style=“font-size: 95%; border: gray solid 1px; border-collapse: collapse;”

'Commande' 'Nom'
'w' variableexpression watch
'W' … delete watch

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
perl/le_debugger.1237078685.txt.gz · Dernière modification : 2009/03/15 00:58 de root