Archives des forums MMO/MMORPG > Neverwinter Nights > NWN - Maskado > [tutorial] Le debugging
[tutorial] Le debugging
Par Azrael07 le 23/1/2003 à 23:29:16 (#3088125)
le debugging consiste a rechercher la provenances d'erreurs dans un code par son execution
de nombreux compilateurs C++, java, puis surtout basic et visual basic utilise des techniques de debugging très évoluées qui permettent de stopper l'execution d'un code a une ligne présise pour en tester les variables.
Seulement nous avec nwn on a pas ca :(
plutot que se lamenter je vais vous donner quelques astuces pour essayer de détecter vos erreurs dans vos scripts en les executants
On va distinguer deux utilisations du débugging
la première va consister a inclure des fonctions temporaires dans le script conserné pour tester ses variables et ses passages ou non dans les différents blocs, et la deuxième va consister a inserer des fonctions indiquants au log ou dm les erreurs de script. Si nombre d'entre vous devez deja utiliser la première technique, la deuxième reste peut etre un peu plus dans l'ombre, mais est fondamentale pour la gestion des scripts un peu gros.
Le debbuging
immaginons un script simple :
void main()
{
int nVariable = GetLocalInt(GetModule(), "VAR");
//plein de lignes de code
//...
//...
//...
//...
//...
if(nVariable == 1)
{
//instructions non executée alors qu'elle aurait du l'être
}
}
dans cet exemple, les instruction dans la boucle ne sont pas executés. La logique va distingué trois possibilités a cette non execution :
1) La variable Locale "VAR" ne contenais pas la valeur 1
2) La variable nVariable ne contenais plus la valeur 1 en arrivant a la condition
3) Les instructions dans la boucle sont invalides et ne peuvent être executées
on va donc voir les techniques pour pouvoir cibler cette erreur.
1) La variable Locale "VAR" contient elle la valeur 1 ?
Pour verifier cela, on va faire un test pour tester cette valeur juste après son affectation, et envoyer en sortie une indication sur le resultat de ce test :
void main()
{
int nVariable = GetLocalInt(GetModule(), "VAR");
if(nVariable == 1)
WriteTimestampedLogEntry("Variable locale VAR initialisée à 1");
else
WriteTimestampedLogEntry("Variable locale VAR differente de 1");
//plein de lignes de code
//...
//...
//...
//...
//...
//...
//...
if(nVariable == 1)
{
//instructions non executée alors qu'elle aurait du l'être
}
a l'execution de ce script, deux solution sont possible : si la variable VAR est initialisée a 1, la fonction est appellée pour inscrire le message dans le fichier log. Dans le cas inverse, et bien une autre fonction est lancée pour idiquer que la variable n'est pas bonne
Si le log montre "Variable locale VAR differente de 1" cela montre que l'erreur ne vient pas de ce script, la variable avait la valeur différente de 1 lors d'une initialisation posterieure
A l'inverse, si le log montre "Variable locale VAR initialisée à 1", notre script par du bon pied et l'erreur recherchée se trouve forcement plus loins
il nous reste a tester les lignes suivantes
2) La variable nVariable contenais elle la valeur 1 en arrivant a la condition
et bien la, de la meme manière nous allons tester le script pendant la boucle pour vérifier ca :
void main()
{
int nVariable = GetLocalInt(GetModule(), "VAR");
//plein de lignes de code
//...
//...
//...
//...
//...
//...
//...
if(nVariable == 1)
{
WriteTimestampedLogEntry("Variable variable initialisée à 1");
//instructions non executée alors qu'elle aurait du l'être
}
else WriteTimestampedLogEntry("Variable nVariable differente de 1");
de la meme facon qu'au dessus, si notre log montre "Variable variable initialisée à 1" cela signifie que la variable nVariable contient bien la valeur 1, dans le cas contraire notre erreur se situe entre l'initialisation de la variable nVariable a partir de la variable locale et la boucle.
pi ben si on a toujours une variable initialisée a 1 on a forcement affaire au 3), il faudrat donc chercher dans les lignes de code du bloc du test ouske ca merde
voila, avec cette méthode on a put centrer le problème sur des zones plus petites, et bien sur on peut ajouter encore autant de test qu'on veut pour trouver ouske ca va pas.
L'affichage des variables est aussi d'une grande utilité pour le debugging. Je vais pas reprendre d'expemple, je pense que vous commencez a saisir le principe mais vous pouvez utilisez les fonctions d'affichage dans le log pour afficher des variables.
WriteTimestampedLogEntry("nVariable = " + IntToString(nVariable);//On affiche dans le log la valeur de la fonction nVariable
Bon après évidamment y'a vraiment plein de variantes dans l'utilisation du debugging, mais vous avez deja le principe de base, on va pouvoir maintenant parler du debugging a grande echelle et les routines de gestion d'erreurs.
PS : tout au long de l'exemple j'utilise la fonction WriteTimestampedLogEntry, c'est pour faire propre, mais dans la pratique si vous lancer le module juste dans le but de debugger votre script vous avez tout interet a faire un truc bourrin et pas bo mais autrement plus rapide :
SendMessageToPC(GetFirstPC(), sMessage);
Les routines de gestion d'erreurs :
Cette technique est utilisée pour les beta test, voir meme pour l'execution du module en version finale pour retrouver les derniers petits bugs. Si vous bossez comme scripteur dans un persistant, il est quasiment fondamental de mettre des fonctions de routines d'erreur.
cela va consister a indiquer dans les logs, au mj ou meme aux joueurs dans erreurs les plus graves qu'une erreur s'est produite.
pour cela j'utilise dans mon module une fonction simple, je vous la donne en bonus (et sans copyrights mdr)
//Affiche un message d'erreur sError dans le fichier .log
//nDMs : Envoie le message sError aux DMs si TRUE
//nPlayers : Envoie le message aux joueurs si TRUE
void ERROR(string sError, int nDMs = TRUE)
{
WriteTimestampedLogEntry(sError);
if(nDMs) SendMessageToAllDMs(sError);
}
bon la fonction n'est pas dure a saisir.
Je l'utilise par exemple lors d'un retour de fonction invalide. Je donne un exemple simpple et j'explique après
//C'est une fonction simple que j'utilise pour retrouver la ville natale du joueur.
//Cette fonction permet de nombreuses erreurs de sortie, son utilisation est donc appropriée
object GetHomeCity(object oPlayer)
{
object oCity = GetLocalObject(oPlayer, "HomeCity");
return oCity;
}
un mauvais retour de la fonction (c'est a dire un objet invalid) signifie que oCreature n'a pas de localObject de type "HomeCity"
dans le contexte de la fonction on peut signaler deux cas :
ou oPlayer n'est pas un joueur, et n'a donc jamais eut a avoir de fonction "HomeCity", ou bien oPlayer est bien un joueur mais ne possède pas de variable de type "HomeCity"
on va donc utiliser la fonction ci dessus associée avec des test pour indiquer l'erreur qui a eut lieux.
object GetHomeCity(object oPlayer)
{
if(!GetIsPC(oPlayer))
{
ERROR("retour de la fonction GetHomeCity invalide: l'objet n'est pas un joueur");//Pour faire ca très bien on peut aussi renvoyer le tag de l'objet
return OBJECT_INVALID;
}
object oCity = GetLocalObject(oPlayer, "HomeCity");
if(!GetIsObjectValid(oCity))
{
ERROR("retour de la fonction GetHomeCity invalide: le joueur ne possède pas de variable de type HomeCity");//la idem, renvoyer le nom du joueur serais plus pratique
return OBJECT_INVALID;
}
return oCity;
}
Voila, je pense que vous comprenez l'interet de la gestion des erreurs, faire un rapport de bug est alors a la portée de n'importe qui.
Plutot que dire : heu.... pas normal la, y'a le perso qui bouge pas alors k'il devrais se barrer, ben les beta testeurs vont pouvoir dire : attention, la fonction GetTruc a un retour invalide de de type....
Ca sauve la vie quand on a une erreur qui peut se trouver quelques part dans 60 pages de ligne de code ^_^'.
Voila pour ce tut, je pense que beaucoup d'entre vous connaissiez deja pas mal de trucs la dedans, mais y auras peut etre quelques infos a grapiller par ci par la ;)
Comme toujours si vous avez des questions ne vous privez pas ;)
JOL Archives 1.0.1
@ JOL / JeuxOnLine