=encoding iso-8859-1 =head1 NAME/NOM perldebtut - Tutoriel de débogage de Perl =head1 DESCRIPTION Une (très) légère introduction sur l'utilisation du débogueur de Perl, et un pointeur vers des sources d'information appronfondies disponibles sur le sujet du débogage des programmes perl. Il y a un nombre extraordinaire de personnes qui semblent ne rien connaître de l'utilisation de débogueur de Perl, bien qu'ils utilisent ce language quotidiennement. Ceci est pour eux. =head1 Utiliser strict (use strict) Tout d'abord vous pouvez, sans utiliser le débogueur perl, mettre en oeuvre quelques idées pour vous simplifier la vie quand arrive le moment de déboguer des programmes perl. En guise de démonstration, voici un script simple, nommé "salut", présentant un problèmeE: #!/usr/bin/perl $var1 = 'Salut le monde'; # toujours voulu faire cela :-) $var2 = "$varl\n"; print $var2; exit; Bien que ce script se compile et s'exécute gaiement, il ne va probablement pas faire ce que l'on en attend, c'est à dire qu'il ne va pas du tout écrire "Salut le monde\n"; il va en revanche faire exactement ce que nous lui avons demandé de faire, les ordinateurs ayant légèrement tendance à agir de cette manière. Ce script va donc écrire un caractère de nouvelle ligne, et vous allez obtenir ce qui ressemble à une ligne blanche. Il semble que le script ne contiennent que deux variables, alors qu'il en contient en fait trois (à cause de la faute de frappe). $var1 = 'Hello World'; $varl = undef; $var2 = "\n"; Afin de mettre en évidence ce type de problème, nous pouvons forcer la déclaration de chaque variable avant son utilisation en faisant appel au module «EstrictE» par l'insertion après la première ligne du script de l'instruction 'use strict'. Maintenant, lors de l'exécution, perl se plaint de trois variables non déclarées, et nous obtenons quatre messages d'erreur car une variable est référencée deux foisE: Global symbol "$var1" requires explicit package name at ./t1 line 4. Global symbol "$var2" requires explicit package name at ./t1 line 5. Global symbol "$varl" requires explicit package name at ./t1 line 5. Global symbol "$var2" requires explicit package name at ./t1 line 7. Execution of ./hello aborted due to compilation errors. S Et pour supprimer ces erreurs, nous déclarons explicitement toutes les variables. Maintenant notre script ressemble à ceciE: #!/usr/bin/perl use strict; my $var1 = 'Salut le monde'; my $varl = undef; my $var2 = "$varl\n"; print $var2; exit; Nous procédons alors à une vérification de la syntaxe (toujours une bonne idée) avant d'exécuter à nouveau le scriptE: > perl -c hello hello syntax OK Et maintenant quand nous exécutons ce script, nous obtenons toujours "\n", mais au moins nous savons pourquoi. La compilation du script a révélé la variable '$varl' (avec la lettre 'l') et le simple changement de $varl par $var1 résoud le problème. =head1 Observer les variables et -w et v D'accord, mais comment faire lorsque vous souhaitez réellement voir vos données, le contenu d'une variable dynamique, juste avant son utilisationE? #!/usr/bin/perl use strict; my $key = 'welcome'; my %data = ( 'this' => qw(that), 'tom' => qw(and jerry), 'welcome' => q(Hello World), 'zip' => q(welcome), ); my @data = keys %data; print "$data{$key}\n"; exit; Tout semble correct. Après avoir été testé avec la vérification de syntaxe (B) nous exécutons ce script et tout ce que nous obtenons est à nouveau une ligne blancheE! Hmmmmm. Dans ce cas, une approche courante du débogage serait de parsemer délibérement quelques instructions print, pour ajouter une première vérification juste avant d'écrire nos données, et une seconde juste aprèsE: print "All OK\n" if grep($key, keys %data); print "$data{$key}\n"; print "done: '$data{$key}'\n"; Après une nouvelle tentativeE: > perl data All OK done: '' Après s'être usé les yeux sur ce code pendant un bon moment, nous allons boire une tasse de café et nous tentons une nouvelle approche. C'est-à-dire que nous appelons la cavalerie en invoquant perl avec l'option 'B<-d>' sur la ligne de commande. > perl -d data Default die handler restored. Loading DB routines from perl5db.pl version 1.07 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(./data:4): my $key = 'welcome'; Donc, ce que nous venons de faire ici est de lancer notre script avec le débogueur intégré de perl. Celui-ci s'est arrêté à la première ligne de code exécutable et attend notre action. Avant de poursuivre plus avant, vous allez souhaiter connaître comment quitter le débugueurE: tapez simplement la lettre 'B', pas les mots 'B', ni 'B'. DB<1> q > C'est cela, vous êtes à nouveau au paddock. =head1 Aide (help) Démarrer à nouveau le débogueur et nous allons examiner le menu d'aide. Il y a plusieurs méthodes pour afficher l'aideE: taper simplement 'B' affichera une longue liste déroulante, 'B<|h>' (pipe-h) redirigera l'aide vers l'afficheur page par page ('more' ou 'less' probablement), et finalement, 'B' (h-espace-h) vous donnera un écran très utile résumant les commandesE: DB<1> h List/search source lines: Control script execution: l [ln|sub] List source code T Stack trace - or . List previous/current line s [expr] Single step [in expr] v [line] View around line n [expr] Next, steps over subs f filename View source in file Repeat last n or s /pattern/ ?patt? Search forw/backw r Return from subroutine M Show module versions c [ln|sub] Continue until position Debugger controls: L List break/watch/actions o [...] Set debugger options t [expr] Toggle trace [trace expr] <[<]|{[{]|>[>] [cmd] Do pre/post-prompt b [ln|event|sub] [cnd] Set breakpoint ! [N|pat] Redo a previous command B ln|* Delete a/all breakpoints H [-num] Display last num commands a [ln] cmd Do cmd before line = [a val] Define/list an alias A ln|* Delete a/all actions h [db_cmd] Get help on command w expr Add a watch expression h h Complete help page W expr|* Delete a/all watch exprs |[|]db_cmd Send output to pager ![!] syscmd Run cmd in a subprocess q or ^D Quit R Attempt a restart Data Examination: expr Execute perl code, also see: s,n,t expr x|m expr Evals expr in list context, dumps the result or lists methods. p expr Print expression (uses script's current package). S [[!]pat] List subroutine names [not] matching pattern V [Pk [Vars]] List Variables in Package. Vars can be ~pattern or !pattern. X [Vars] Same as "V current_package [Vars]". i class inheritance tree. y [n [Vars]] List lexicals in higher scope . Vars same as V. e Display thread id E Display all thread ids. For more help, type h cmd_letter, or run man perldebug for all docs. Plus d'options que vous ne le pensiezE! Ce n'est pas si terrible qu'il y parait, et il est très utile et en plus amusant, d'en connaître plus à propos de chacune de ces commandes. Il y a quelques options utiles à connaître immédiatement. Vous pensez probablement que vous n'utilisez aucune bibliothèque en ce moment, mais 'B' va vous montrer les modules actuellement actifs, ceux appelés aussi bien par le débogueur que par le script. De même, 'B vous affichera les méthodes et 'B' tous les sous-programmes (par motif) comme ci-dessous. 'B' et 'B' montrent les variables du programme rangées par package et peuvent être restreintes par l'utilisation de motifs. DB<2>S str dumpvar::stringify strict::bits strict::import strict::unimport L'utilisation de 'B' et de ses cousins demande de ne pas utiliser l'identificateur de type ($@%), seulement le 'nom'E: DM<3>X ~err FileHandle(stderr) => fileno(2) Rappelez-vous que nous sommes dans notre minuscule programme avec un problème, nous devrions jeter un oeil pour voir où nous sommes et à quoi nos données ressemblent. Avant tout, regardons un peu de code autour de notre position actuelle (la première ligne de code dans notre cas), grâce à la lettre 'B'E: DB<4> v 1 #!/usr/bin/perl 2: use strict; 3 4==> my $key = 'welcome'; 5: my %data = ( 6 'this' => qw(that), 7 'tom' => qw(and jerry), 8 'welcome' => q(Hello World), 9 'zip' => q(welcome), 10 ); À la ligne 4 se présente un pointeur utile, qui vous indique où vous vous situez actuellement. Pour voir plus avant dans le programme, tapons encore 'B'. DB<4> w 8 'welcome' => q(Hello World), 9 'zip' => q(welcome), 10 ); 11: my @data = keys %data; 12: print "All OK\n" if grep($key, keys %data); 13: print "$data{$key}\n"; 14: print "done: '$data{$key}'\n"; 15: exit; Et si vous voulez lister à nouveau la ligne 5, tapez 'l 5', (remarquez l'espace)E: DB<4> l 5 5: my %data = ( Dans le cas de ce script, il n'y pas grand chose à voir, mais il peut y avoir des pages entières à explorer et 'B' peut être très utile. Pour réinitialiser votre vue sur la ligne qui va s'exécuter, tapez un simple point '.'E: DB<5> . main::(./data_a:4): my $key = 'welcome'; La ligne affichée est la prochaine qui sera exécutée, sans que cette exécution ne se soit produite pour l'instant. Donc bien que nous puissions imprimer une variable grâce à la commande 'B

', tout ce que nous allons obtenir est une valeur vide (non définie). Ce que nous devons faire est exécuter la prochaine instruction exécutable en tapant 'B'E: DB<6> s main::(./data_a:5): my %data = ( main::(./data_a:6): 'this' => qw(that), main::(./data_a:7): 'tom' => qw(and jerry), main::(./data_a:8): 'welcome' => q(Hello World), main::(./data_a:9): 'zip' => q(welcome), main::(./data_a:10): ); Maintenant, nous pouvons observer cette première variable ($clef) DB<7> p $key welcome La ligne 13 est celle où l'action a lieu, donc continuons notre progression grâce à la lettre 'B', qui insert un point d'arrêt 'à usage unique' à la ligne ou au sous-programme indiqué. DB<8> c 13 All OK main::(./data_a:13): print "$data{$key}\n"; Nous avons dépassé notre vérification (où 'All OK' était imprimé) et nous nous sommes arrêtés juste avant le gros du travail. Nous pourrions essayer de lister quelques variables pour voir ce qu'il se passe. DB<9> p $data{$key} Pas grand chose ici, observons notre hachageE: DB<10> p %data Hello Worldziptomandwelcomejerrywelcomethisthat DB<11> p keys %data Hello Worldtomwelcomejerrythis Bon, ceci n'est pas très facile à lire, et dans l'écran d'aide (B), la commande 'B' semble prometteuseE: DB<12> x %data 0 'Hello World' 1 'zip' 2 'tom' 3 'and' 4 'welcome' 5 undef 6 'jerry' 7 'welcome' 8 'this' 9 'that' Ceci n'aide pas beaucoup, deux 'bienvenue', mais sans que l'on sache s'ils correspondent à des clefs ou à des valeurs. Il s'agit juste de l'affichage d'une image d'un tableau, dans ce cas sans grande utilité. L'astuce est ici d'utiliser une référence à la structure de donnéesE: DB<13> x \%data 0 HASH(0x8194bc4) 'Hello World' => 'zip' 'jerry' => 'welcome' 'this' => 'that' 'tom' => 'and' 'welcome' => undef La référence est réellement détaillée, et nous mettons finalement en évidence notre problème. Notre citation est parfaitement valide, mais erronée pour notre propos, avec 'and jerry' traité comme deux mots séparés plutôt que comme une phrase, donc entraînant un mauvais alignement des paires du hachage. Le commutateur 'B<-w>' nous aurait indiqué ce problème si nous l'avions utilisé au départ, et nous aurait évité pas mal de soucisE: > perl -w data Odd number of elements in hash assignment at ./data line 5. Nous réparons notre citationE: 'tom' =Eq(et jerry), et exécutons à nouveau notre programme. Nous obtenons alors le résultat attenduE: > perl -w data Bonjour le monde Pendant que nous y sommes, regardons de plus près la commande 'B'. Elle est réellement utile et vous affichera merveilleusement le contenu de reférences imbriquées, d'objet complets, de fragments d'objets, de tout ce que vous voudrez bien lui appliquerE: Construisons rapidement un objet, et e-x-plorons le. D'abord, démarrons le débogueurE: il demande une entrée à partir de STDIN, donnons-lui donc quelque chose qui n'engage à rien, un zéroE: > perl -de 0 Default die handler restored. Loading DB routines from perl5db.pl version 1.07 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(-e:1): 0 Maintenant construisez au vol un objet sur quelques lignes (notez l'antislash)E: DB<1> $obj = bless({'unique_id'=>'123', 'attr'=> \ cont: {'col' => 'black', 'things' => [qw(this that etc)]}}, 'MY_class') Et jetez un oeil dessusE: DB<2> x $obj 0 MY_class=HASH(0x828ad98) 'attr' => HASH(0x828ad68) 'col' => 'black' 'things' => ARRAY(0x828abb8) 0 'this' 1 'that' 2 'etc' 'unique_id' => 123 DB<3> Utile, n'est-ce pasE? Vous pouvez utiliser eval sur n'importe quoi, et expérimenter avec des fragments de programme ou d'expressions régulières jusqu'à ce que les vaches rentrent à l'étableE: DB<3> @data = qw(this that the other atheism leather theory scythe) DB<4> p 'saw -> '.($cnt += map { print "\t:\t$_\n" } grep(/the/, sort @data)) atheism leather other scythe the theory saw -> 6 Si vous voulez voir l'historique des commandes, tapez un 'B'E: DB<5> H 4: p 'saw -> '.($cnt += map { print "\t:\t$_\n" } grep(/the/, sort @data)) 3: @data = qw(this that the other atheism leather theory scythe) 2: x $obj 1: $obj = bless({'unique_id'=>'123', 'attr'=> {'col' => 'black', 'things' => [qw(this that etc)]}}, 'MY_class') DB<5> Et si vous voulez répéter une quelques commande précédente, utilisez le point d'exclamationE:'B'E: DB<5> !4 p 'saw -> '.($cnt += map { print "$_\n" } grep(/the/, sort @data)) atheism leather other scythe the theory saw -> 12 Pour plus d'informations sur les références, voyez les pages de manuel perlref et perlreftut. =head1 Pas à pas dans le code Voici un simple programme qui converti les degrés Fahrenheit en Celsius (et vice-versa). Ce programme présente aussi un problème. #!/usr/bin/perl -w use strict; my $arg = $ARGV[0] || '-c20'; if ($arg =~ /^\-(c|f)((\-|\+)*\d+(\.\d+)*)$/) { my ($deg, $num) = ($1, $2); my ($in, $out) = ($num, $num); if ($deg eq 'c') { $deg = 'f'; $out = &c2f($num); } else { $deg = 'c'; $out = &f2c($num); } $out = sprintf('%0.2f', $out); $out =~ s/^((\-|\+)*\d+)\.0+$/$1/; print "$out $deg\n"; } else { print "Usage: $0 -[c|f] num\n"; } exit; sub f2c { my $f = shift; my $c = 5 * $f - 32 / 9; return $c; } sub c2f { my $c = shift; my $f = 9 * $c / 5 + 32; return $f; } Pour une raison inconnue, la conversion des Fahrenheit en Celsius ne donne pas le résultat escompté. Voici ce que ce programme effectueE: > temp -c0.72 33.30 f > temp -f33.3 162.94 c Pas très cohérentE! Nous allons placer manuellement un point d'arrêt dans le programme et exécuter celui-ci dans le débogueur pour voir ce qui se passe. Un point d'arrêt est un drapeau. Le débogueur va exécuter le programme sans interruption jusqu'à ce drapeau. Quand il l'atteint, il arrête l'exécution et présente une invite pour la suite de l'interaction. En utilisation normale, ces commandes de débogage sont totalement ignorées, et elle peuvent être conservées en toute sécurité dans le programme final, bien quelles en augmentent quelque peu le désordre. my ($in, $out) = ($num, $num); $DB::single=2; # insert at line 9! if ($deg eq 'c') ... > perl -d temp -f33.3 Default die handler restored. Loading DB routines from perl5db.pl version 1.07 Editor support available. Enter h or `h h' for help, or `man perldebug' for more help. main::(temp:4): my $arg = $ARGV[0] || '-c20'; Nous allons simplement descendre jusqu'à notre point d'arrêt prédéfini grâce à la commande 'B'E: DB<1> c main::(temp:10): if ($deg eq 'c') { suivie par une commande 'B' pour voir où nous sommesE: DB<1> v 7: my ($deg, $num) = ($1, $2); 8: my ($in, $out) = ($num, $num); 9: $DB::single=2; 10==> if ($deg eq 'c') { 11: $deg = 'f'; 12: $out = &c2f($num); 13 } else { 14: $deg = 'c'; 15: $out = &f2c($num); 16 } et par une commande 'B

' pour montrer le contenu des variables que nous utilisonsE: DB<1> p $deg, $num f33.3 Nous pouvons placer un autre point d'arrêt à n'importe quelle ligne commencant par un numéro suivi par deux points. Nous utiliserons la ligne 17, cette ligne étant la première après la sortie du sous-programme f2c. Nous prévoyons de faire une pause à cette endroit plus tard. DB<2> b 17 Il n'y a pas d'indications en retour, mais vous pouvez voir les points d'arrêt placés en utilisant la commande 'B'. DB<3> L temp: 17: print "$out $deg\n"; break if (1) Il est à noter que les points d'arrêts peuvent être supprimés en utilisant les commandes 'B' ou 'B'. Nous allons alors continuer plus avant dans notre sous-programme, cette fois en utilisant, à la place du numéro de ligne, le nom du sous-programme suivi par la commande 'B', maintenant familière, DB<3> c f2c main::f2c(temp:30): my $f = shift; DB<4> v 24: exit; 25 26 sub f2c { 27==> my $f = shift; 28: my $c = 5 * $f - 32 / 9; 29: return $c; 30 } 31 32 sub c2f { 33: my $c = shift; Notez que si il existait un appel à un sous-programme entre notre position et la ligne 29, et si nous voulions traverser ce sous-programme pas-à-pas, nous pourrions utiliser la commande 'B'. Pour le franchir, il faudrait utiliser 'B' qui exécuterait alors le sous-programme sans l'inspecter. Dans notre cas, nous continuons simplement jusqu'à la ligne 29. Regardons la valeur retournéeE: DB<5> p $c 162.944444444444 Ce n'est pas du tout la réponse exacte, mais l'opération semble correcte. Je me demande si le problème ne serait pas lié aux priorités des opérateursE? Essayons notre opération avec quelques autres combinaisons de parenthèses. DB<6> p (5 * $f - 32 / 9) 162.944444444444 DB<7> p 5 * $f - (32 / 9) 162.944444444444 DB<8> p (5 * $f) - 32 / 9 162.944444444444 DB<9> p 5 * ($f - 32) / 9 0.722222222222221 :-) Ce dernier essai ressemble plus à ce que l'on souhaiteE! Bien, maintenant nous pouvons définir la variable à renvoyer, et nous allons sortir de notre sous-programme grâce à la commande 'B'E: DB<10> $c = 5 * ($f - 32) / 9 DB<11> r scalar context return from main::f2c: 0.722222222222221 Semble correct, continuons donc jusqu'à la fin du scriptE: DB<12> c 0.72 c Debugged program terminated. Use q to quit or R to restart, use O inhibit_exit to avoid stopping after program termination, h q, h R or h O to get additional info. Une correction rapide de la ligne erronée (insertion des parenthèses manquantes) dans le programme et nous avons terminé. =head1 Emplacement réservé pour a, w, t, T Action, observation des variables, suivi des piles etc...: sur la liste des choses à faire. a w t T =head1 Expressions rationnelles (ou regulières) Avez-vous jamais voulu savoir à quoi ressemble une expression régulièreE? Vous aurez besoin pour cela de compiler Perl avec le drapeau DEBUGGINGE: > perl -Dr -e '/^pe(a)*rl$/i' Compiling REx `^pe(a)*rl$' size 17 first at 2 rarest char at 0 1: BOL(2) 2: EXACTF (4) 4: CURLYN[1] {0,32767}(14) 6: NOTHING(8) 8: EXACTF (0) 12: WHILEM(0) 13: NOTHING(14) 14: EXACTF (16) 16: EOL(17) 17: END(0) floating `'$ at 4..2147483647 (checking floating) stclass `EXACTF ' anchored(BOL) minlen 4 Omitting $` $& $' support. EXECUTING... Freeing REx: `^pe(a)*rl$' Vouliez-vous vraiment savoirE?:-) Pour plus de détails sanglants sur la mise en oeuvre des expressions régulières, regardez les pages de manuel perlre, perlretut, et pour décoder les étiquettes mystérieuses (voir ci-dessus BOL, CURLYN etc..), se réferer au manuel perldebguts. =head1 Conseils sur les sorties Pour obtenir toutes les sorties de votre journal d'erreur, et ne manquer aucun messages via les tampons du système d'exploitation, inserez au début de votre script une ligne de ce typeE: $|=1; Pour voir l'extrémité d'un fichier journal en croissance dynamique, tapez à partir de la ligne de commandeE: tail -f $error_log Envelopper tous les appels à 'die' dans une routine peut être utilisé pour voir comment et à partir de quoi ils sont appelés. La page de manuel perlvar présente plus d'informationsE: BEGIN { $SIG{__DIE__} = sub { require Carp; Carp::confess(@_) } } Diverses techniques utilisées pour la redirection des descripteurs STDOUT et STDERR sont expliquées dans les pages de manuel perlopentut et perlfaq8. =head1 CGI Juste un petit truc pour tous les programmeurs CGI qui ne trouvent comment poursuivre après l'invite 'waiting for input' quand ils exécutent leur script à partir de la ligne de commande. Essayez quelque chose commeE: > perl -d my_cgi.pl -nodebug Bien sûr les pages de manuel CGI et perlfaq9 vous en dirons plus. =head1 Interfaces graphiques (GUI) L'interface de la ligne de commande est étroitement intégrée avec une extension à emacs et il existe aussi une interface vers vi. Vous n'avez pas à faire tout ceci à partir de la ligne de commande, il existe quelques possibilités d'utilisation d'interfaces graphiques. Ce qui est agréable alors est de de pouvoir agiter la souris au dessus d'une variable et de voir apparaître dans une fenêtre appropriée ou dans un ballon d'aide son contenu. Plus besoin de se fatiguer à taper 'B' :-) En particulier, vous pouvez partir à la chasse deE: B Enveloppe pour le débogueur intégré basée sur perlTK B data display debugger (débogeur exposeur de données) B et B sont des outils spécifiques à Windows NT. NB. Plus d'informations sur ces outils ainsi que sur d'autres équivalents seraient appréciées. =head1 Résumé Nous avons vu comment encourager de bonnes pratiques de programmation grâce à l'utilisation de B et de B<-w>. Nous pouvons exécuter le débogueur de perl (B) pour inspecter nos données grâce aux commandes B

et B. Vous pouvez parcourir votre code, placer des points d'arrêt avec la commande B, parcourir ce programme pas à pas avec B ou B, continuer avec B et revenir d'un sous-programme avec B. Plutôt intuitif quand on y regarde de près. Il y a bien sûr beaucoup plus à découvrir à propos du débogueur, ce tutoriel n'ayant fait qu'en explorer la surface. La meilleure façon d'en apprendre plus est d'utiliser perldoc pour découvrir beaucoup d'autres d'aspects du langage, de lire l'aide en ligne (la page de manuel perlbug devrait être votre prochaine lecture) et bien sûr d'expérimenter. =head1 VOIR AUSSI Les pages de manuel perldebug, perldebguts, perldiag, perlrun, I. =head1 AUTEUR Richard Foley Copyright(c) 2000 =head1 CONTRIBUTIONS Diverses personnes ont aidées par leurs suggestions et leurs contributions, en particulierE: Ronald J Kimball Hugo van der Sanden Peter Scott =head1 TRADUCTION =head2 Version Cette traduction française correspond à la version anglaise distribuée avec perl 5.8.8. Pour en savoir plus concernant ces traductions, consultez L. =head2 Traducteur Traduction initialeE: Landry Le Chevanton . Mise à jourE: Paul Gaborit . =head2 Relecture Gérard Delafond