gestion des exceptions dans les classes en cpp - C++ - Programmation
Marsh Posté le 26-02-2003 à 08:31:32
humpf, l'access violation ne se traite pas comme une exception normale (enfin, je crois ?). Pour ca faut repasser par une fonction de l'API Windows dont j'ai soigneusement oublie le nom ( )
Marsh Posté le 26-02-2003 à 08:34:15
BlackGoddess a écrit : notamment la gestion des access violation |
Camouflage de bugs ?
Marsh Posté le 26-02-2003 à 08:37:37
BlackGoddess a écrit : notamment la gestion des access violation |
avant de trapper les access violation, demande toi donc plutot pourquoi ils arrivent !
vérifies tes pointeurs, tes allocations mémoire, etc...
Marsh Posté le 26-02-2003 à 09:14:30
chrisbk a écrit : humpf, l'access violation ne se traite pas comme une exception normale (enfin, je crois ?). Pour ca faut repasser par une fonction de l'API Windows dont j'ai soigneusement oublie le nom ( ) |
en C++Builder il suffit de catcher l'exception EAccessViolation, mais bon c'est de la programmation bien dégueux ça
Marsh Posté le 26-02-2003 à 09:17:03
antp a écrit : |
clair, genre je catch tout ca, puis je continue comme si de rien n'etait sans chercher pourquoi le truc m'a exploser au nez
Marsh Posté le 26-02-2003 à 10:08:00
L'un n'empêche pas l'autre, chers amis. N'oubliez jamais qua dans la réalité réelle, on n'est pas toujours maître de l'intégralité du code qu'on utilise (typiquement parce qu'on se repose sur des librairies externes). Pourtant, vous vous devez d'écrire des applications les plus stables possibles, donc capables de récupérer sur toute erreur, même théoriquement impossible (la loi de Murphy traine dans le coin).
Pour verser dans la métaphore automobile, ce n'est pas parce que les conducteurs conduisent plus mal que ce qu'ils devraient, que les constructeurs de voitures doivent s'interdire de développer des systèmes de sécurité passive.
Marsh Posté le 26-02-2003 à 10:42:53
Si cette "exception" n'est pas prevue par la DLL que tu appelles, comment peut tu etre certain du bon fonctionnement de cette meme DLL apres que tu aie intercepte cette exception ?
Les seules utilisations de ce genre de code a mon gout sont :
- Panic save
- Affichage de joli fenetre d'erreur pour fournir de bonnes info de debug
- Code tres tres bas niveau de gestion de memoire ou de pile.
Si l'exception n'etait pas prevue, il est illusoire de continuer l'execution du programe. Il faut au moins decharger la DLL de la memoire ( a supposer que ce soit encore possible )
Marsh Posté le 26-02-2003 à 13:46:05
Harkonnen a écrit : |
Mauvaise langue, il veut simplement formatter correctement le messages d'erreur avant de l'anvoyer à l'utilisateur avec la mention : "please contact your local retailer", comme ça m'est arrivé récement avec Oracle Designer (AssertionViolation).
Enfin c'est le côté optimiste de la spéculation ....
Marsh Posté le 26-02-2003 à 14:10:43
Kristoph a écrit : Si cette "exception" n'est pas prevue par la DLL que tu appelles, comment peut tu etre certain du bon fonctionnement de cette meme DLL apres que tu aie intercepte cette exception ? |
Parce qu'au moins sous Windows, les "segmentation fault" peuvent être récupérées sous la forme d'une exception C++.
De toute façon, en pareil cas, le comportement le plus classique est de rendre compte de l'erreur, d'arrêter le travail en cours, et, s'il y en a une (cas des serveurs ou d'applis à GUI), de revenir à la boucle d'attente générale.
Marsh Posté le 26-02-2003 à 14:32:49
BifaceMcLeOD a écrit : L'un n'empêche pas l'autre, chers amis. N'oubliez jamais qua dans la réalité réelle, on n'est pas toujours maître de l'intégralité du code qu'on utilise (typiquement parce qu'on se repose sur des librairies externes). |
Les seuls interets sont bien de formatter l'erreur et de fermer le programme au plus vite (pour ne pas manipuler des donnees corrompues, ce qui peut etre pire que le remede).
Edit: j'ai corrige une faute de frappe :-)
Marsh Posté le 26-02-2003 à 14:42:32
Pas forcément fermer le programme. Ce n'est pas parce qu'on fonction particulière plante qu'il faut tout arrêter.
Ainsi, si tu écris un serveur, il faut stopper juste le traitement de la requête. Si tu écris une application avec un GUI, juste le traitement en cours, démarré par l'action GUI. Dans tous les cas, essayer de trouver un état du programme qui offre une stabilité globale la plus satisfaisante possible. Et éventuellement, parmi les erreurs (les plus graves), il y a "plus assez de mémoire disponible", qui, là, oui, peut remettre en cause une grosse partie de l'application. Mais d'ici à ce que vous en soyez à traiter ce genre d'erreurs...
Marsh Posté le 26-02-2003 à 15:18:46
BifaceMcLeOD a écrit : Pas forcément fermer le programme. Ce n'est pas parce qu'on fonction particulière plante qu'il faut tout arrêter. |
Si tu sais te relever d'une access violation, je suis preneur.
Mah c'est zouper. Apres, soit tu vas avoir de jolis leaks (pour ce que tu n'as pas pu nettoyer apres le plantage), soit tu vas aller d'acces violation en acces violation (parce que tu n'as pas pu initialiser des variables a cause du plantage).
A moins que tu fasses du code entierement transactionnel a chaque acces de pointeur (j'aimerais bien voir ca !)
Marsh Posté le 26-02-2003 à 15:28:05
kenshiro182 a écrit : |
Evidemment que tu vas avoir des fuites mémoires. Mais en production, ça vaut toujours mieux que de se planter lamentablement. De toute façon, la plupart des clients sont bien conscients que, quand tu leur écris un programme nouveau, au début de la production, le soft ne sera pas d'une stabilité optimale. Mais si tu leur montres que tu as mis en place des mécanismes pour améliorer petit à petit la situation (typiquement avec ce genre de rapports d'erreurs, qui te permettent de diagnostiquer et donc de corriger les erreurs, donc améliorer progressivement la qualité de l'application), ils se montreront compréhensifs et ils accepteront de faire redémarrer l'appli/serveur de temps en temps.
Ceci dit, tu parles de transactionnel, en gros, c'est effectivement ce dont je parle. Mais pas à chaque accès pointeur, gros malin, relis mon post précédent : je parlais de requêtes. Ce sont des traitements très haut niveau dans l'application.
Mais à la limite, chaque couche logicielle doit être susceptible de pouvoir récupérer et traiter ce type d'erreurs inattendues ("exceptionnelles" ) si elle le souhaite. C'est comme cela qu'on fait des applis stables et robustes.
Marsh Posté le 26-02-2003 à 16:05:36
BifaceMcLeOD a écrit : |
Je sais, c'est pas le plus grave, et je sais tres bien que ca peut se surveiller.
Citation : |
Mais vu que tes requetes sont traitees dans le meme processus, et donc dans le meme espace d'adressage, une erreur dans l'une peut amener un ouragan au Japon.
Ce serait en Java OK. En C++, vaut mieux quitter avant qu'une catastrophe arrive.
Marsh Posté le 26-02-2003 à 16:39:55
Tout est une question de granularité de la "transaction"...
Marsh Posté le 26-02-2003 à 18:03:03
BifaceMcLeOD a écrit : Tout est une question de granularité de la "transaction"... |
Et de la quantité de dégats qu'a fait le bug ... Question subsidiaire : quelles sont les garantie offertes par le bug au niveau dégâts ?
Marsh Posté le 26-02-2003 à 18:17:04
Ben après si ton appli n'est pas modulaire, et que tout touche à tout (et réciproquement), ça te regarde...
Marsh Posté le 26-02-2003 à 21:46:49
BifaceMcLeOD a écrit : Ben après si ton appli n'est pas modulaire, et que tout touche à tout (et réciproquement), ça te regarde... |
Si ton programme a un seul process, explique-moi donc comment un accès mémoire n'importe ou peut ne pas déstabiliser le tout. Moi je vois pas.
Marsh Posté le 26-02-2003 à 21:49:33
kenshiro182 a écrit : |
Peut être avec des sémaphores savamment disposés ?
Marsh Posté le 27-02-2003 à 08:45:15
Harkonnen a écrit : |
Si tu as un pointeur fou, je ne vois pas ce que des semaphores vont changer.
Marsh Posté le 27-02-2003 à 08:59:28
kenshiro182 a écrit : |
T'as pas tout à fait tort
Ceci dit, on papote beaucoup sur ce topic, mais l'auteur n'y a toujours rien posté hormis sa question...
Marsh Posté le 27-02-2003 à 10:22:22
kenshiro182 a écrit : |
Je suppose évidemment que l'application tourne sur un OS qui supporte le mode protégé. Autrement dit, si tu essaies de lire ou d'écrire à l'adresse 0 (ou 0xdeadbeef ), ou de manière générale, à n'importe quelle adresse qui n'a pas été allouée à ton process, l'OS refuse tout simplement d'exécuter l'instruction et la suite du programme, et il te renvoie une exception violente (genre SIGSEGV sous Unix). Rien n'est déstabilisé à cet instant, simplement l'OS se protège des déstabilisations futures. Et c'est précisément parce qu'il s'en est protégé et qu'il t'en avertit que tu peux, si tu le souhaite, récupérer la situation et faire en sorte que ton application tourne toujours.
Or la majorité des OS modernes supporte le mode protégé, on n'est plus au temps de MS-DOS. Et bien sûr, ce mécanisme est d'autant plus puissant que le programmeur adopte une certaine discipline de programmation (la réinitialisation systématique des pointeurs a une assez bonne efficacité, par exemple).
Marsh Posté le 27-02-2003 à 11:48:36
Et les pointeurs fous qui pointent vers une zone deja liberee ?
Et les double desallocation ?
Et les buffer overflow ?
Et les corruption de pile ?
Marsh Posté le 27-02-2003 à 12:44:44
Les zones libérées ne sont plus allouées pour ton processus. Tout accès à ces zones mémoires se soldera par un SEGV, preuve que l'OS a bien détecté l'erreur avant qu'elle n'arrive réellement. S'il n'y a pas de SEGV, c'est soit qu'en fait la zone n'a pas été désallouée, soit qu'elle a été réallouée pour le même processus entre temps (ce qui peut arriver aussi, j'en conviens). Mais dans ce dernier cas, l'application met en danger la cohérence des résultats qu'elle rend, pas nécessairement sa stabilité globale -- et en tout cas, jamais la stabilité du reste du système.
Les doubles désallocations sont protégées si tu réinitialises tes pointeurs à NULL après toute désallocation.
Les écrasements mémoire liés aux dépassements de zones tampons rentrent dans la même catégorie que les pointeurs fous : soit tu es hors zone allouée et la sanction est immédiate, soit tu te retrouves dans une autre zone mémoire qui t'a été allouée, et de toute façon, tu ne détectes pas l'erreur tout de suite.
Enfin, au sujet des corruptions de pile, je ne vois pas en quoi ça rentre en ligne de compte. Même si théoriquement, c'est possible, en pratique, tu n'as quasiment aucune chance de taper dedans avec des pointeurs -- à moins de le faire vraiment exprès -- ou alors, c'est que tu fais des longjumps, ce que quasiment aucune application ne fait (je connais les cas où on en a besoin ; en gros quand tu écris un OS ou un mini-OS : or ce n'est pas la majorité des applications...).
De toute façon, tu pourras toujours trouver des cas où l'application n'arrive pas à rétablir la situation. Mais quand on écrit une application industrielle, c'est toujours préférable qu'elle soit la plus robuste possible, quitte à ce que de temps en temps, elle annonce à l'utilisateur un message du genre "Panic: unrecoverable error", plutôt que de s'arrêter brutalement et systématiquement à chaque fois que l'état de l'application "risque" d'être corrompu. 90 % de chances d'arriver à se rétablir vaut toujours mieux qu'aucune. Et puis au pire quelle sera la sanction si l'application n'arrive pas à se rétablir ? Elle s'arrêtera, comme si elle n'avait pas essayé de se rétablir. Mais le reste du système ne sera pas plus en danger. L'avantage, c'est que l'application aura survécu à 10 bugs au lieu de mordre la poussière dès le premier, et l'utilisateur aura pu l'utiliser 10 fois plus longtemps.
La plupart d'entre nous ici sommes des utilisateurs intensifs d'ordinateurs, et nous avons tous pestés contre ces logiciels qui "plantaient tout le temps". Eviter les plantages inopinés est un travail délicat, certes, mais y réfléchir est une nécessité absolue si l'on veut que dans les années qui viennent, la stabilité générale des applications progresse. Car ce n'est que par la modification des habitudes de programmation qu'on y arrivera.
J'insiste : il faut y réfléchir. Eventuellement en n'acceptant, au début, de n'y arriver que très partiellement. Très partiellement vaudra toujours mieux que pas du tout. Mais surtout pas évacuer d'emblée la question en se disant que ce n'est pas possible.
Marsh Posté le 27-02-2003 à 14:27:25
BifaceMcLeOD a *crit : Les zones lib*r*es ne sont plus allou*es pour ton processus. Tout acc*s * ces zones m*moires se soldera par un SEGV, preuve que l'OS a bien d*tect* l'erreur avant qu'elle n'arrive r*ellement. S'il n'y a pas de SEGV, c'est soit qu'en fait la zone n'a pas *t* d*sallou*e, soit qu'elle a *t* r*allou*e pour le m*me processus entre temps (ce qui peut arriver aussi, j'en conviens). Mais dans ce dernier cas, l'application met en danger la coh*rence des r*sultats qu'elle rend, pas n*cessairement sa stabilit* globale -- et en tout cas, jamais la stabilit* du reste du syst*me. |
Si le SIGSEGV viens d'une sous partie separee de l'application ( un plugin WinAmp par exemple ), il est tout a fait possible que le systeme soit prevu pour decharger le module fautif de la memoire sans planter tout l'application.
Mais si l'erreur viens d'une partie du prog qui n'est pas separee du coeur principal, je refuse de prendre le risque de continuer a faire tourne le prog dans un etat bancal. Ce qui peut arriver est tres varie, vu qu'on parle d'un risque de corruption memoire, il peut tout arriver. Par exemple :
- L'appli plante, ignore l'erreur, mais le mechanisme de sauvegarde est corrompu, tu peux travailler pendant 2 heures avant de te rendre compte qu'il faut redemarer ton appli avant de pouvoir sauvegarder.
- Les donnee sur lequel tu travailles sont corrompues, mais la sauvegarde marche. Pas de chance le document produit ne peux pas etre ouvert lui.
- La pile est corrompue, la gestion des exceptions ne marche plus dans ce cas !
- Utilisation d'une zone memoire desalouee, puis reallouee derriere : corruption d'une partie du programme qui ne te concerne pas.
- Utilisation d'une zone memoire desalouee/double free : risque de corruption de ton allocateur memoire. Si tu ne comprends pas ce que je dis, va voir le code source d'un allocateur memoire comme DougLea malloc pour comprendre comment ca fonctionne.
Au final, sauf si le programme a pris ses precautions pour isoler un process particulier, tu ne peux repondre de rien en cas de SIGSEGV. Au mieux, tu fais un panic save et tu quittes !
Marsh Posté le 27-02-2003 à 14:32:30
Kristoph a écrit : |
3ds max 4 fait un panic save meme si c juste mon plug d'export qui plante. Mais c vrai que le fait qu'il te previenne que ca foire est largement mieux que les precedentes versions qui se contentaient de te claquer la porte au nez
Marsh Posté le 27-02-2003 à 16:06:13
Kristoph a écrit : |
J'ai l'impression que nous n'avons pas la même interprétation de ce qu'est un SIGSEGV. Un SIGSEGV arrive quand le processeur refuse d'exécuter une instruction, parce qu'il pressent un accès mémoire interdit. Donc, il n'y a pas de catastrophe qui soit déjà arrivée, et la mémoire n'est pas dans un état "bancal", puisque précisément le signal est là pour éviter qu'elle le soit. Par contre, c'est clair qu'elle le sera si tu continues tes traitements comme de rien n'était.
Mais un SEGV n'est fondamentalement pas différent d'une division par zéro ! Or ça ne te gêne pas de récupérer sur division par zéro, n'est-ce pas ? Pourquoi serait-ce différent avec un accès mémoire incorrect ?
Au passage, tu as l'air de dire que je ne sais pas de quoi je parle, pourtant, je peux t'affirmer que je parle d'expérience. J'ai déjà participé à des projets, développés en C/C++, dont un des objectifs primordiaux était la robustesse. Certes, il a fallu mettre en place des mécanismes auxquels la plupart des développeurs C/C++ n'étaient pas habitués, mais la reprise sur erreur fonctionnait plutôt bien. Parce que l'application était conçue pour être robuste (et pourtant tout fonctionnait dans un seul process Unix/NT).
Marsh Posté le 27-02-2003 à 16:23:26
BifaceMcLeOD a *crit : |
Je vois ce que tu veux dire. Ce qui m'ennuie, c'est que dans un programme bien ecris, les SIGSEGV n'arrivent pas. Et que quand un SIGSEGV arrive, ca peut tout aussi bien etre l'utilisation d'un pointeur NULL que le resultat d'une erreur de programation qui ne declenche pas de SIGSEGV, mais qui a corrompu l'etat du programme. Et plus tard, quand le code utilisant les zones memoire affectee se declenche, SIGSEGV.
Dans ce dernier cas qui est de loin le plus courrant, quand le SIGSEGV se declenche, il est deja bien trop tard.
Marsh Posté le 28-02-2003 à 10:25:07
Ben je me répète, mais avec quelques règles simples de programmation (comme l'initialisation/réinitialisation systématique des pointeurs), et un bon choix de grosseur de granularité dans les actions à aborter, l'application survit très bien, car sa mémoire n'est jamais corrompue dans ses données vitales.
Marsh Posté le 28-02-2003 à 10:42:53
chrisbk a écrit : |
je confime
Marsh Posté le 25-02-2003 à 22:31:53
Bonjour, je chercherais un cours, un tutorial ou un exemple pour la gestion des exceptions dans une classe, notamment la gestion des access violation
merci
---------------
-( BlackGoddess )-