Bug indétectable via printf() / cout

Le
AG
Bonjour à tous,

Je travaille dans une équipe de personnes pour lesquels le débugger
n'est pas l'outil qui tombe sous le sens lorsqu'il s'agit de trouver
les bugs des outils qu'ils ont développé.

Afin de les sensibiliser, je recherche un exemple de bug difficile
(voir impossible) a détecter via des printf()/cout qui n'afficheraient
que les contenus des variables. (Je me rends bien compte qu'on doit
pouvoir tout débugger avec printf()/cout, mais il faut parfois
afficher les adresses des variable (en plus de leur valeur), voir
parfois la mémoire à certains endroits.)

Je voudrais construire un exemple simple (notions de C++ pas trop
compliquées) et court (un seul fichier, 100 lignes max) pour qu'on
puisse l'étudier en un quart d'heure, mais le débugger en un temps
presque infini sans débugger.

Il est possible que l'exemple, puisqu'il y a un bug, dépende de la
plateforme mais c'est un peu inévitable.

L'idéal serait que le plantage ait lieu bien après le bugça rajout=
e
du piment. Bref, vous voyez l'idée quoi

J'avais plusieurs pistes d'exploitation de bug:

Piste 1:
Boucle for décroissante sur un entier non signé : for(size_t i = N;
0<= i; i--)

Piste 2:
comparaison de double : double x; ; x == 0.0

Piste 3:
retour de malloc() non testé

Piste 4:
caractère de fin de ligne non testé ()

Mais jusque là, je crains qu'il ne soit trop facile de détecter le
bug

J'ai donc ensuite pensé aux références. Mais ce code est encore un pe=
u
trop voyant. Dans le main(), on peut facilement se demander pourquoi
f1 et f2 sont des références, ce qui met directement la puce à
l'oreille. Peut être trouvez vous ce code bien trop compliqué, ou
auriez vous en tête un manière de "l'améliorer" :-)

A bon entendeur salut.

AG.




#include <iostream>

using namespace std;

#define BUFFER_LENGTH 10

template<int N, class T>
class fifo_circular
{
private:
T * position;
size_t pos_offset;
T buffer[N];

public:
fifo_circular() { pos_offset = N-1; position = buffer + pos_offset;
for(int i=0;i<N;i++) buffer[i]=T(0);};

T step(T &input)
{
*position = input;

if(pos_offset>0) // ici il y aurait peut être moyen de glisser le
bug de la piste 1
pos_offset--;
else
pos_offset = N-1;

position = buffer + pos_offset;
return *position;
};

template<int M, class U> friend ostream & operator<<(ostream & o,
const fifo_circular<M,U> &f);
};

template<int N, class T>
fifo_circular<N,T> & Init(T & value)
{
fifo_circular<N,T> & f = fifo_circular<N,T>(); // hé hé hé

for(size_t i = 0; i < N; i++)
f.step(value);
return f;
}

template<int M, class U>
ostream & operator<<(ostream & o, const fifo_circular<M,U> &f)
{
for(size_t i = 0; i < M; i++)
o << f.buffer[i] << " ";
return o;
}

int main(int argc, char * argv[])
{
int a = 1;
fifo_circular<5,int> & f1 = Init<5,int>(a); // ici, c'est un peu trop
voyant. On se demande pourquoi f1 est déclaré en tant que
référence
a = 2;
fifo_circular<5,int> & f2 = Init<5,int>(a);

cout << "fifo f1: " << f1 << "";
cout << "fifo f2: " << f2 << "";

return 0;
}
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 15
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Marc Boyer
Le #20664081
Le 30-11-2009, AG
Je travaille dans une équipe de personnes pour lesquels le débugger
n'est pas l'outil qui tombe sous le sens lorsqu'il s'agit de trouver
les bugs des outils qu'ils ont développé.



Mais est-ce grave ? J'avoue ne quasiment jamais me servir
de débugger. Je travaille surtout en précondition/invariant
(assert) + tests unitaires, et éventuellement valgrind
quand je soupsonne une corruption mémoire.

Et une fois le bug identifié, à grand coups de cerr
+ assert.

D'autant que souvent, le bug n'apparait pas en mode débug ;-)

Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
AG
Le #20664071
On Nov 30, 2:27 pm, Marc Boyer wrote:
  Mais est-ce grave ? J'avoue ne quasiment jamais me servir
de débugger. Je travaille surtout en précondition/invariant
(assert) + tests unitaires, et éventuellement valgrind
quand je soupsonne une corruption mémoire.

  Et une fois le bug identifié, à grand coups de cerr
+ assert.

  D'autant que souvent, le bug n'apparait pas en mode débug ;-)



Bonjour Marc,

Je pense que c'est grave effectivement. Tout ce que tu écris pour
débugger est inutile avec un débugger. C'est donc un gain de temps
immédiat.
Ensuite d'aucun diront que leur debugger (type gdb) est mal
pratique...peut être. Mais avec un débugger pratique, c'est un gain de
temps certain.

Cela dit, qu'on me le dise si les debugger ne sont pas intensivement
utilisés dans l'industrie, je serais content de me tromper. Dans mon
entourage, tout ceux à qui j'ai montré un debugger pratique l'ont
adopté.

Alexandre.

PS: tes élèves, tu ne leur apprends pas le débugger ?
Jean-Marc Bourguet
Le #20664281
AG
Cela dit, qu'on me le dise si les debugger ne sont pas intensivement
utilisés dans l'industrie, je serais content de me tromper.



Ca depend des gens, du probleme a debugger et du domaine. En general, je
trouve que j'abandonne de debuggeur pour autre chose trop lentement plutot
que trop vite. Et plus le probleme est complexe, moins ils sont utiles.
Un systeme de log et des assertions bien concues sont plus efficaces pour
les vrais problemes compliques.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
JKB
Le #20664481
Le 30-11-2009, ? propos de
Re: Bug indétectable via printf() / cout,
AG ?crivait dans fr.comp.lang.c++ :
On Nov 30, 2:27 pm, Marc Boyer wrote:
  Mais est-ce grave ? J'avoue ne quasiment jamais me servir
de débugger. Je travaille surtout en précondition/invariant
(assert) + tests unitaires, et éventuellement valgrind
quand je soupsonne une corruption mémoire.

  Et une fois le bug identifié, à grand coups de cerr
+ assert.

  D'autant que souvent, le bug n'apparait pas en mode débug ;-)



Bonjour Marc,

Je pense que c'est grave effectivement. Tout ce que tu écris pour
débugger est inutile avec un débugger. C'est donc un gain de temps
immédiat.
Ensuite d'aucun diront que leur debugger (type gdb) est mal
pratique...peut être. Mais avec un débugger pratique, c'est un gain de
temps certain.

Cela dit, qu'on me le dise si les debugger ne sont pas intensivement
utilisés dans l'industrie, je serais content de me tromper. Dans mon
entourage, tout ceux à qui j'ai montré un debugger pratique l'ont
adopté.

Alexandre.

PS: tes élèves, tu ne leur apprends pas le débugger ?



Bonjour,

Je ne programme pas en C++, tout au plus je corrige des choses parce
que j'ai une certaine phobie de la programmation objet. Néanmoins, en C
si j'utilise assez régulièrement gdb/ddd, c'est toujours après avoir
généré un core pour l'analyser. J'ai un tas de bugs sous la main qui
ne se produisent _jamais_ lorsque je lance le truc à débugguer sous
un quelconque debugger (code multithreadé, choses bizarres sur des
variables, mutexes, sémaphores...). En général, je préfère une macro()
style assert() qui me crache sur la sortie quelques informations et qui
envoie un SIGBUS pour générer un core, un peu la même technique que Marc.

Au pire, j'utilise valgrind lorsqu'il est disponible voire des
choses comme efence lorsque je suis sur une architecture non
supportée par valgrind.

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Marc Boyer
Le #20664471
Le 30-11-2009, AG
On Nov 30, 2:27 pm, Marc Boyer wrote:
  Mais est-ce grave ? J'avoue ne quasiment jamais me servir
de débugger. Je travaille surtout en précondition/invariant
(assert) + tests unitaires, et éventuellement valgrind
quand je soupsonne une corruption mémoire.

  Et une fois le bug identifié, à grand coups de cerr
+ assert.

  D'autant que souvent, le bug n'apparait pas en mode débug ;-)



Bonjour Marc,

Je pense que c'est grave effectivement. Tout ce que tu écris pour
débugger est inutile avec un débugger. C'est donc un gain de temps
immédiat.



Mais tout ce que tu fais avec ton débuger est non rejouable
(à moins que tu ne scriptes ton deboggeur, mais j'ai rencontré
peu de personnes qui le fassent).

En plus, les precond/invariants servent aussi de documentation,
et puis tu les écris quand tu écris la fonctions, donc tu sais ce
que ça va faire.

Et puis c'est aussi facile d'écrire
PRINT_VALUE(toto)
avec une macro du type
#define PRINT_VALUE(X) if (debug) { cerr<<#X "="<<(X)<<endl;}
que de faire click-gauche, 'suivre-variable'

Et puis les débogueurs que je connaissais ne montraient pas d'historique.
Souvent, la question c'est "pourquoi cette var vaut ça ?", et tu remontes
le temps. En affichant les variables en console, la console se souvient.

De plus, en ce qui concerne les fuites mémoires, valgrind est
bien plus efficace que de suivre pas à pas le débogueur.

Ensuite d'aucun diront que leur debugger (type gdb) est mal
pratique...peut être. Mais avec un débugger pratique, c'est un gain de
temps certain.



Possible. J'utilisais le deboggeur avant, sous Win* il y a plus
de 10 ans, puis SunStudio puis ddd/gdb/xemacs, puis plus rien...

Cela dit, qu'on me le dise si les debugger ne sont pas intensivement
utilisés dans l'industrie, je serais content de me tromper. Dans mon
entourage, tout ceux à qui j'ai montré un debugger pratique l'ont
adopté.



On verra ce que disent les autres contributeurs. Je ne parle que de
mon expérience.

PS: tes élèves, tu ne leur apprends pas le débugger ?



Je leur apprenais, oui, mais pas avec gdb/ddd, ils y perdaient trop
de temps: avancer pas à pas, saisir les entrées, faire un "run-until",
voir qu'on est allé trop loin, recommencer, re-saisir les entrées...

Non, tests unitaires / assert / printf, c'était la méthodologie.

On présentait gdb/ddd en illustration des cours sur l'allocation
dynamique, pour qu'ils "voient" leurs listes chainées.

Et quand en projet ils galéraient à rechercher un bug, et qu'ils
finissaient par m'appeller, au bord de la crise, parfois me montraient
leur problème avec gdb/ddd, mais toujours, j'allais à la pèche aux
infos à coup d'assert/printf, et, éventuellement valgrind.

Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
Marc Boyer
Le #20664651
Le 30-11-2009, JKB

Je ne programme pas en C++, tout au plus je corrige des choses parce
que j'ai une certaine phobie de la programmation objet. Néanmoins, en C
si j'utilise assez régulièrement gdb/ddd, c'est toujours après avoir
généré un core pour l'analyser.



Oui, j'allais oublié le pb du crash et des analyses post-mortem.

J'ai un tas de bugs sous la main qui
ne se produisent _jamais_ lorsque je lance le truc à débugguer sous
un quelconque debugger (code multithreadé, choses bizarres sur des
variables, mutexes, sémaphores...). En général, je préfère une macro()
style assert() qui me crache sur la sortie quelques informations et qui
envoie un SIGBUS pour générer un core, un peu la même technique que Marc.



Oui, je me souviens aussi de code multi-thread temps-réel qui se bloque,
qui ne tourne pas sous le débogueur car tout y est trop lent, et qui
ne se bloque pas quand on ajoute les printfs (surement car ça ajoutes
des commutation de contexte)...

Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
Senhon
Le #20664641
"AG"
On Nov 30, 2:27 pm, Marc Boyer wrote:

Je pense que c'est grave effectivement. Tout ce que tu écris pour
débugger est inutile avec un débugger. C'est donc un gain de temps
immédiat.
Ensuite d'aucun diront que leur debugger (type gdb) est mal
pratique...peut être. Mais avec un débugger pratique, c'est un gain de
temps certain.




Un debugger est un outils indispensable.
Personnellement, je regretterai ( expérience déjà vécu ) amèrement, de ne
pas disposer d'un debugger. Cela ne m'empêche pas d'utiliser, aussi, des
cout pour la mise au point.
Les deux se complémentent.

GDB, rend d'énormes services.
Mais, je n'ai rien trouvé de mieux que Visual Studio, c'est un plaisir que
de travailler avec cette merveille. GDB en comparaison fait outil d'un autre
âge.
Marc Boyer
Le #20664631
Le 30-11-2009, AG
J'ai donc ensuite pensé aux références. Mais ce code est encore un peu
trop voyant. Dans le main(), on peut facilement se demander pourquoi
f1 et f2 sont des références, ce qui met directement la puce à
l'oreille. Peut être trouvez vous ce code bien trop compliqué, ou
auriez vous en tête un manière de "l'améliorer" :-)



Tellement voyant que mon g++ le voit:
error: invalid initialization of non-const reference of type ‘fifo_circular<5, int>&’ from a temporary of type ‘fifo_circular<5, int>’


Marc Boyer
--
En prenant aux 10% des francais les plus riches 12% de leurs revenus,
on pourrait doubler les revenus des 10% les plus pauvres.
http://www.inegalites.fr/spip.php?article1&id_mot0
espie
Le #20664891
In article AG
On Nov 30, 2:27 pm, Marc Boyer wrote:
  Mais est-ce grave ? J'avoue ne quasiment jamais me servir
de débugger. Je travaille surtout en précondition/invariant
(assert) + tests unitaires, et éventuellement valgrind
quand je soupsonne une corruption mémoire.

  Et une fois le bug identifié, à grand coups de cerr
+ assert.

  D'autant que souvent, le bug n'apparait pas en mode débug ;-)



Bonjour Marc,

Je pense que c'est grave effectivement. Tout ce que tu écris pour
débugger est inutile avec un débugger. C'est donc un gain de temps
immédiat.



assert() est de toutes facons toujours utile, puisqu'il permet de
detecter certains problemes avant qu'ils ne deviennnet des vrais bugs.

Il y a un risque a trop utiliser un debugguer: c'est une bequille trop
pratique, parfois. On debranche le cerveau, on ecrit n'importe quoi, et
on se dit "c'est pas grave, on debugguera apres..."

mais c'est sur que c'est quand meme super-pratique comme outil.

(nota: mode debug + optimisations, bien sur, histoire d'avoir le meme
code. Si ca donne pas le meme code, le compilateur est a jeter.)
Fabien LE LEZ
Le #20665361
On Mon, 30 Nov 2009 05:20:46 -0800 (PST), AG
Je travaille dans une équipe de personnes pour lesquels le débugger
n'est pas l'outil qui tombe sous le sens lorsqu'il s'agit de trouver
les bugs des outils qu'ils ont développé.



J'avoue que je partage un peu leur avis.
Le débugger est un outil extrêmement pratique pour comprendre d'où
viennent certains bugs coriaces. Mais je rencontre ces bugs assez
rarement, c'est pourquoi je ne sors mon débugger que quelques fois
dans l'année.

Piste 3:
retour de malloc() non testé

Piste 4:
caractère de fin de ligne non testé ()



Ces deux-là sont extrêmement rares en C++ :
- à ma connaissance, la seule utilité de malloc() est de
permettre la création de son propre opérateur new, ce qu'on ne fait
pas tous les jours ;
- les histoires de '', c'est la cuisine interne de
std::string, et a priori, ce n'est pas toi (ni moi) qui l'écris.


je recherche un exemple de bug difficile
(voir impossible) a détecter via des printf()/cout qui n'afficheraient
que les contenus des variables.



As-tu rencontré un exemple dans la vraie vie ? Si oui, ressors-le,
nettoie-le un peu, et tu as ce qu'il te faut.
Si tu n'en as pas en stock, et que tu n'en trouves pas un dans les six
semaines (par exemple), c'est que le débugger n'est pas si
indispensable que tu le pensais ;-)
Publicité
Poster une réponse
Anonyme