IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Gestion des exceptions et performances


Sujet :

C++

  1. #1
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut Gestion des exceptions et performances
    Bonjour à tous,

    Je travaille sur une application embarquée et je m'intéresse à la gestion des erreurs. J'ai la chance de pouvoir travailler en C++ et non strictement en C.

    L'idée est de pouvoir remonter jusqu'à l'origine d'un problème en production via des logs erreurs pour se concentrer sur cette origine sans devoir reproduire le cas exécutif que l'on a du mal à reproduire sur un environnement de développement où on a le "tout confort".

    Il faut donc générer une espèce de stack trace comme on peut avoir en Java ou en PHP, mais avec les contraintes de performances quant au matériel utilisé qui peut être de l'Android sur des vieux téléphones, du Raspberry etc.

    En gros, il faut savoir dire "Echec de création du contexte XXX parce que ci, parce que ça, parce que ci, parce que ça, parce que ci, parce que ça, parce que le malloc a échoué à cet endroit là parce que plus assez de mémoire", ou "fichier YYY introuvable".

    Si ça consomme beaucoup de ressources quand il y a une erreur, c'est pas grave, mais il ne faut pas que ça pollue les performances lorsqu'il n'y en a pas, et il faut savoir que l'exécutif va dealer avec des données externes qui peuvent comporter des erreurs, l'un des but est de remonter l'erreur à l'entité qui l'a produite.

    J'y vois deux manière de faire :
    - Les codes retour de chaque fonction et plein de logs pour suivre le fil exécutif sur papier
    - Via une stack d'exceptions à coups de macros __FILE__, __LINE__ et __FUNCTION__ où chaque exceptions entretiendra l'exception à l'origine de celle en court

    Pour la première méthode, ça va consommer du disque et des performances même quand tout se passe on ne peut mieux, inenvisageable en production dans ce contexte là, sans compter le temps passé à analyser des logs de 30.000 lignes pour se concentrer sur la dizaine qui nous intéresse, d'autant plus que pour plus de visibilité il faut gérer du padding, décomposer les fichiers, les supprimer quand ils deviennent "trop anciens" etc. C'est pas tip top.

    Pour la seconde, il y aura plein de try / catch partout mais ça c'est pas grave on saura quoi faire pour rendre les choses agréable, juste que j'ai entendu dire que la gestion des exceptions consommait des ressources de manière significative, mais est-ce vrai ou juste une idée reçue sans fondement ?

    Merci à vous,

    A bientôt
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Normalement, les exceptions on un coût nul dans les fonctions traversées quand il n'y a pas d'exception lancée. Mais :
    - Il est possible qu'il y ait un coût à chaque try/catch, je ne sais pas trop. Par exemple en réduisant les possibilités d'optimisation.
    - Il n'est pas impossible que les compilateurs des environnements que tu utilises n'implémentent pas les exceptions de la manière la plus optimisée qui soit, car il existe une certaine résistance culturelle à l'utilisation des exceptions dans l'embarqué
    - Le coût quand une exception est lancée est borné, mais difficile à estimer. Si tu as des contraintes temps réel strictes, ça peut devenir gênant.

    Dans ton cas, j'utiliserais probablement des exceptions, mais je regarderais s'il n'y a pas moyen, au lieu de mettre des try/catch partout, d'utiliser des méthodes non portables pour récupérer une représentation de la call stack (ou créer un crash dump) au moment où l'exception est lancée.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Pour le fait d'empiler des contextes, c'est un truc que j'ai envisagé à une époque (mais jamais eu à mettre en œuvre parce qu'on a migré vers .Net peu après).
    L'avantage, c'est que si ces toi qui contrôles les lancements d'exceptions, cela peut se faire sans rajouter des try/catch: L'important est qu'à chaque construction d'exceptions, tu récupères la stack trace ainsi construite et que tu la copies dans l'exception.
    Au niveau des coûts en performance:
    • D'abord, tu as la gestion des exceptions/destructeurs par le code lui-même. Évidemment ça dépend des architectures, mais:
      • sous Windows en x86, toute fonction qui contient des destructeurs ou un try/catch doit elle/même maintenir une liste chaînée de contextes, avec le pointeur "sommer de pile" stocké en thread-local storage. Cela est un petit overhead, mais dans ton exemple il le "comptera" que si la fonction modifiée n'avait pas déjà un try/catch ou une variable locale avec destructeur.
      • Sous Windows en x86-64, la gestion d'exception n'utilise pas de liste chaînée de contexte, mais une table statique d'information pour la remontée, créée par le compilo. Avec ça, le coût du lancement d'exception est probablement plus élevé mais les try/catch et destructeurs sont gratuits.
    • Dans ta liste de contextes, tu vas devoir faire la même chose: Maintenir une liste chaînée. Deux méthodes:
      • Passer le chaînage explicitement dans en paramètre de toutes tes fonctions (par exemple, systématiquement en premier). L'avantage majeur de cela, c'est que tu n'auras pas besoin d'ajouter un destructeur dans ta classe de contexte (utile si elle ne stocke pas de chaînes mais seulement des const char* pour __FILE__ et __FUNCTION__ par exemple). L'inconvénient majeur, c'est qu'il faut modifier toutes les fonctions!
      • Utiliser une variable thread-locale pour le pointeur de "sommet de pile": C'est plus lourd et ça nécessite un destructeur pour "dépiler" ton contexte en fin de fonction, mais ça permet de ne pas avoir à modifier les prototypes (et en plus, si tu passes par des fonctions que tu ne contrôles pas, ça ne gênera pas ta pile de contextes à part la présence d'un "chaînon manquant").
    • Et au moment où l'exception est créée et lancée, il y a le coût de la copie du contexte dans l'exception. Mais vu que ça n'arrive que dans le cas exceptionnel, généralement à partir du moment où on est dans ce cas les performances sont "fair game".
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #4
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    J'éviterai des try-catch à chaque niveau. Et comme Loic, j'utiliserai des fonctions non portables (il doit exister des encapsulations portables en revanche il me semble) pour obtenir la callstack au moment du throw. En revanche, pour avoir plus de détails, ça va être compliqué. A moins effectivement de créer un core (chose qui peut prendre pas mal de temps (ça peut se compter en dizaines de secondes)).

    A noter, que l'activation des exceptions grossi la taille de l'exécutable. Ca, c'est certain. Pour le coût comparé à un code correct (i.e. avec un if toutes les deux lignes), je n'ai jamais croisé jusqu'à présent de comparaison sérieuse. Effectivement, un code avec exception peut couter plus cher qu'un code au pays magique où les erreurs ne sont jamais rattrapées.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  5. #5
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Merci pour vos réponses,

    Pour obtenir une call stack en bon uniforme même avec des fonctions systèmes de l'environnement visé, j'imagine qu'il faut compiler avec les symboles qu'il faut afin que ces fonctions puissent reporter un minimum d'info, donc avec les symboles de debug.

    Si on compile avec les options d'optimisation ça risque d'être compliqué d'avoir les éléments nécessaires à la construction des traces étant donné que c'est du compilé et ni de l'interprété, ni du semi interprété.

    Du coup j'ai bien envie de la jouer à coups de macro même si je n'aime pas bien ça mais au moins ça permet d'uniformiser le code sans trop le polluer et d'activer / désactiver cette fonctionnalité lors de la compilation en fonction de ce que l'on a besoin selon la maturité à l'exécution.

    Va falloir la jouer ingénieux j'ai l'impression, à coup de techniques batardes telles que les macro..
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Tu peux écrire une classe qui serait vide quand tu compiles en Release, ça te permettrait de n'avoir des macros qu'à un seul endroit.

    Si les constructeurs et destructeur vides sont directement dans le fichier d'en-tête, ils seront inlinés en "rien du tout"...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Pour info, au sujet de libunwind: http://eli.thegreenplace.net/2015/pr...ll-stack-in-c/ (mais il est probable effectivement que -g soit de mise ; je n'ai pas testé)
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  8. #8
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Je vais checker les possibilités de libunwind.

    Apparemment il faut les symboles de debug mais à la rigueur ça c'est pas bien grave. Reste à voir comment ça fonctionne en multi thread et s'il y a moyen de l'utiliser sur des architectures arm 32 / 64.

    Merci
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 074
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 074
    Points : 12 120
    Points
    12 120
    Par défaut
    Pour information, sous Windows, un programme avec les informations de "débug" (les symboles) compilé avec VS ne pèse que quelque octets de plus (le chemin vers le PDB généré dans un segment non exécuté).
    L'utilisation/génération d'un coredump sous Windows ne coute rien en terme de performance ( un minidump, c'est 64Ko et uniquement sur plantage), et cela ne demande aucune modification dans le code source et aucune pollution dans celui-ci.

    Vous disposerez de toutes les informations demandées au moment du crash, pas des données "historiques".

    C'est tellement efficace sous Windows que j'essayerai de voir s'il n'y pas l'équivalent sur les plateformes cibles.

    Pour les aspects temporels, plutôt que de tracer tous les appels, je pense qu’instrumentaliser les appels qui passent d'un module logique à un autre serait plus efficace.
    Cette instrumentalisation peut ressembler aux MACRO utilisées lors des appels vers des fonctions en Dll. (avec possibilité d'utiliser des prologues et épilogues d'appels customisés).

Discussions similaires

  1. [ADOConnect] gestion des exception en tout temps
    Par portu dans le forum Bases de données
    Réponses: 1
    Dernier message: 20/04/2005, 19h01
  2. [ORACLE 9i] Gestion des exceptions
    Par sygale dans le forum SQL
    Réponses: 6
    Dernier message: 19/08/2004, 15h06
  3. Gestion des exception (EOleException)
    Par shurized dans le forum Bases de données
    Réponses: 5
    Dernier message: 30/06/2004, 17h25
  4. [XMLRAD] gestion des exceptions
    Par pram dans le forum XMLRAD
    Réponses: 2
    Dernier message: 28/01/2003, 17h48
  5. c: gestion des exceptions
    Par vince_lille dans le forum C
    Réponses: 7
    Dernier message: 05/06/2002, 14h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo