Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Bug indétectable via printf() / cout

148 réponses
Avatar
AG
Bonjour =E0 tous,

Je travaille dans une =E9quipe de personnes pour lesquels le d=E9bugger
n'est pas l'outil qui tombe sous le sens lorsqu'il s'agit de trouver
les bugs des outils qu'ils ont d=E9velopp=E9.

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

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

Il est possible que l'exemple, puisqu'il y a un bug, d=E9pende de la
plateforme mais c'est un peu in=E9vitable.

L'id=E9al serait que le plantage ait lieu bien apr=E8s le bug...=E7a rajout=
e
du piment. Bref, vous voyez l'id=E9e quoi...

J'avais plusieurs pistes d'exploitation de bug:

Piste 1:
Boucle for d=E9croissante sur un entier non sign=E9 : for(size_t i =3D N;
0<=3D i; i--)

Piste 2:
comparaison de double : double x; ... ; x =3D=3D 0.0

Piste 3:
retour de malloc() non test=E9

Piste 4:
caract=E8re de fin de ligne non test=E9 (\0)

Mais jusque l=E0, je crains qu'il ne soit trop facile de d=E9tecter le
bug

J'ai donc ensuite pens=E9 aux r=E9f=E9rences. 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=E9f=E9rences, ce qui met directement la puce =E0
l'oreille. Peut =EAtre trouvez vous ce code bien trop compliqu=E9, ou
auriez vous en t=EAte un mani=E8re de "l'am=E9liorer" :-)

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 =3D N-1; position =3D buffer + pos_offset;
for(int i=3D0;i<N;i++) buffer[i]=3DT(0);};

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

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

position =3D 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 =3D fifo_circular<N,T>(); // h=E9 h=E9 h=E9

for(size_t i =3D 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 =3D 0; i < M; i++)
o << f.buffer[i] << " ";
return o;
}

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

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

return 0;
}

10 réponses

1 2 3 4 5
Avatar
Marc Boyer
Le 30-11-2009, AG a écrit :
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
Avatar
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.
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 ?
Avatar
Jean-Marc Bourguet
AG writes:

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
Avatar
JKB
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.
Avatar
Marc Boyer
Le 30-11-2009, AG a écrit :
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
Avatar
Marc Boyer
Le 30-11-2009, JKB a écrit :

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
Avatar
Senhon
"AG" a écrit dans le message de groupe de discussion :

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.
Avatar
Marc Boyer
Le 30-11-2009, AG a écrit :
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
Avatar
espie
In article ,
AG wrote:
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.)
Avatar
Fabien LE LEZ
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 ;-)
1 2 3 4 5