J'ai du code C ++ hérité dont je suis censé supprimer le code inutilisé. Le problème est que la base de code est grande.
Comment savoir quel code n'est jamais appelé / jamais utilisé?
c++
optimization
dead-code
user63898
la source
la source
f()
et qu'un appel àf()
résout sans ambiguïté au 1er, alors il n'est pas possible de résoudre cet appel au 2e juste en ajoutant une 3e fonction nomméef()
- le "pire que vous puissiez faire" "en ajoutant que la 3ème fonction est de rendre l'ambiguïté de l'appel et donc d'empêcher la compilation du programme. Aimerait (= être horrifié) pour voir un contre-exemple.Réponses:
Il existe deux variétés de code inutilisé:
Pour le premier type, un bon compilateur peut aider:
-Wunused
(GCC, Clang ) devrait avertir des variables inutilisées, l'analyseur Clang inutilisé a même été incrémenté pour avertir des variables qui ne sont jamais lues (même si elles sont utilisées).-Wunreachable-code
(ancien GCC, supprimé en 2010 ) devrait mettre en garde contre les blocs locaux qui ne sont jamais accessibles (cela se produit avec des retours précoces ou des conditions qui ont toujours la valeur true)catch
blocs inutilisés , car le compilateur ne peut généralement pas prouver qu'aucune exception ne sera levée.Pour le deuxième type, c'est beaucoup plus difficile. Statistiquement, cela nécessite une analyse complète du programme, et même si l'optimisation du temps de liaison peut réellement supprimer le code mort, dans la pratique, le programme a été tellement transformé au moment où il est exécuté qu'il est presque impossible de transmettre des informations significatives à l'utilisateur.
Il existe donc deux approches:
gcov
. Notez que des drapeaux spécifiques doivent être passés pendant la compilation pour que cela fonctionne correctement). Vous exécutez l'outil de couverture de code avec un bon ensemble d'entrées variées (vos tests unitaires ou tests de non-régression), le code mort est nécessairement dans le code non atteint ... et vous pouvez donc commencer à partir d'ici.Si vous êtes extrêmement intéressé par le sujet et que vous avez le temps et l'envie de travailler vous-même sur un outil, je vous suggère d'utiliser les bibliothèques Clang pour construire un tel outil.
Étant donné que Clang analysera le code pour vous et effectuera une résolution de surcharge, vous n'aurez pas à gérer les règles des langages C ++ et vous pourrez vous concentrer sur le problème actuel.
Cependant, ce type de technique ne peut pas identifier les remplacements virtuels qui ne sont pas utilisés, car ils pourraient être appelés par un code tiers que vous ne pouvez pas raisonner.
la source
foo()
d'être marqué comme "appelé" alors qu'il n'apparaît qu'enif (0) { foo(); }
serait un bonus mais nécessiterait des astuces supplémentaires.)Pour le cas des fonctions entières inutilisées (et des variables globales inutilisées), GCC peut en fait faire la plupart du travail pour vous à condition d'utiliser GCC et GNU ld.
Lors de la compilation de la source, utilisez
-ffunction-sections
et-fdata-sections
, puis lors de la liaison de l'utilisation-Wl,--gc-sections,--print-gc-sections
. L'éditeur de liens répertorie désormais toutes les fonctions qui pourraient être supprimées car elles n'ont jamais été appelées et tous les globaux qui n'ont jamais été référencés.(Bien sûr, vous pouvez également ignorer la
--print-gc-sections
partie et laisser l'éditeur de liens supprimer les fonctions en silence, mais les conserver dans la source.)Remarque: cela ne trouvera que les fonctions complètes inutilisées, cela ne fera rien sur le code mort dans les fonctions. Les fonctions appelées à partir de code mort dans les fonctions en direct seront également conservées.
Certaines fonctionnalités spécifiques à C ++ causeront également des problèmes, en particulier:
Dans les deux cas, tout ce qui est utilisé par une fonction virtuelle ou un constructeur à variable globale doit également être conservé.
Une mise en garde supplémentaire est que si vous créez une bibliothèque partagée, les paramètres par défaut de GCC exporteront toutes les fonctions de la bibliothèque partagée, ce qui la rendra "utilisée" en ce qui concerne l'éditeur de liens. Pour résoudre ce problème, vous devez définir la valeur par défaut pour masquer les symboles au lieu d'exporter (en utilisant par exemple
-fvisibility=hidden
), puis sélectionner explicitement les fonctions exportées que vous devez exporter.la source
Eh bien, si vous utilisez g ++, vous pouvez utiliser ce drapeau
-Wunused
Selon la documentation:
http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html
Edit : Voici un autre indicateur utile
-Wunreachable-code
Selon la documentation:Mise à jour : j'ai trouvé un sujet similaire Détection de code mort dans un projet C / C ++ hérité
la source
-Wunused
met en garde contre les variables qui sont déclarées (ou déclarées et définies en une seule fois) mais qui n'ont jamais été utilisées. Soit dit, assez ennuyeux avec les gardes de portée: p Il existe une implémentation expérimentale dans Clang pour l'avertir également pour les variables non volatiles qui sont écrites mais jamais lues (par Ted Kremenek).-Wunreachable-code
met en garde contre le code dans une fonction qui ne peut être atteint, il peut être code situé après unethrow
oureturn
déclaration ou un code dans une branche qui est jamais pris ( ce qui arrive en cas de comparaisons tautologiques) par exemple.Je pense que vous cherchez un outil de couverture de code . Un outil de couverture de code analysera votre code pendant son exécution et vous permettra de savoir quelles lignes de code ont été exécutées et combien de fois, ainsi que celles qui ne l'ont pas été.
Vous pouvez essayer de donner une chance à cet outil de couverture de code open source: TestCocoon - outil de couverture de code pour C / C ++ et C #.
la source
void func()
dans a.cpp, qui est utilisée dans b.cpp. Comment le compilateur peut vérifier que func () est utilisé dans le programme? C'est le travail des linkers.La vraie réponse ici est: vous ne pouvez jamais vraiment savoir avec certitude.
Au moins, pour les cas non triviaux, vous ne pouvez pas être sûr d'avoir tout compris. Tenez compte des éléments suivants de l'article de Wikipedia sur le code inaccessible :
Comme Wikipedia le note correctement, un compilateur intelligent peut être capable d'attraper quelque chose comme ça. Mais considérons une modification:
Le compilateur comprendra-t-il cela? Peut être. Mais pour ce faire, il faudra faire plus que courir
sqrt
contre une valeur scalaire constante. Il devra comprendre que ce(double)y
sera toujours un entier (facile), puis comprendre la plage mathématique desqrt
l'ensemble des entiers (dur). Un compilateur très sophistiqué pourrait être en mesure de le faire pour lasqrt
fonction, ou pour chaque fonction dans math.h , ou pour toute fonction à entrée fixe dont il peut déterminer le domaine. Cela devient très, très complexe et la complexité est fondamentalement illimitée. Vous pouvez continuer à ajouter des couches de sophistication à votre compilateur, mais il y aura toujours un moyen de se faufiler dans du code qui sera inaccessible pour un ensemble donné d'entrées.Et puis il y a les jeux d'entrée qui ne sont tout simplement jamais saisis. Entrée qui n'aurait aucun sens dans la vie réelle ou qui serait bloquée par une logique de validation ailleurs. Il n'y a aucun moyen pour le compilateur de les connaître.
Le résultat final de cela est que, bien que les outils logiciels mentionnés par d'autres soient extrêmement utiles, vous ne saurez jamais avec certitude que vous avez tout attrapé à moins de parcourir manuellement le code par la suite. Même alors, vous ne serez jamais certain de ne rien manquer.
La seule vraie solution, à mon humble avis, est d'être aussi vigilant que possible, d'utiliser l'automatisation à votre disposition, de refactoriser où vous le pouvez et de rechercher constamment des moyens d'améliorer votre code. Bien sûr, c'est une bonne idée de le faire de toute façon.
la source
Je ne l'ai pas utilisé moi-même, mais cppcheck prétend trouver des fonctions inutilisées. Cela ne résoudra probablement pas le problème complet, mais ce pourrait être un début.
la source
cppcheck --enable=unusedFunction --language=c++ .
pour trouver ces fonctions inutilisées.Vous pouvez essayer d'utiliser PC-lint / FlexeLint de Gimple Software . Il prétend
Je l'ai utilisé pour l'analyse statique et je l'ai trouvé très bon, mais je dois admettre que je ne l'ai pas utilisé pour trouver spécifiquement du code mort.
la source
Mon approche normale pour trouver des trucs inutilisés est
watch "make 2>&1"
a tendance à faire l'affaire sur Unix.C'est un processus assez long, mais qui donne de bons résultats.
la source
Marquez autant de fonctions et de variables publiques que privées ou protégées sans provoquer d'erreur de compilation, tout en faisant cela, essayez également de refactoriser le code. En rendant les fonctions privées et dans une certaine mesure protégées, vous avez réduit votre zone de recherche car les fonctions privées ne peuvent être appelées qu'à partir de la même classe (sauf s'il existe des macros stupides ou d'autres astuces pour contourner la restriction d'accès, et si c'est le cas, je vous recommanderais trouver un nouvel emploi). Il est beaucoup plus facile de déterminer que vous n'avez pas besoin d'une fonction privée car seule la classe sur laquelle vous travaillez actuellement peut appeler cette fonction. Cette méthode est plus facile si votre base de code a de petites classes et est faiblement couplée. Si votre base de code n'a pas de petites classes ou a un couplage très serré, je suggère de les nettoyer d'abord.
La prochaine étape consistera à marquer toutes les fonctions publiques restantes et à créer un graphique d'appel pour comprendre la relation entre les classes. À partir de cet arbre, essayez de déterminer quelle partie de la branche semble pouvoir être coupée.
L'avantage de cette méthode est que vous pouvez le faire par module, il est donc facile de continuer à passer votre test sans avoir une longue période lorsque vous avez une base de code cassée.
la source
Si vous êtes sous Linux, vous voudrez peut-être
callgrind
un outil d'analyse de programme C / C ++ qui fait partie de lavalgrind
suite, qui contient également des outils qui vérifient les fuites de mémoire et autres erreurs de mémoire (que vous devriez également utiliser). Il analyse une instance en cours d'exécution de votre programme et produit des données sur son graphe d'appel et sur les coûts de performance des nœuds sur le graphe d'appel. Il est généralement utilisé pour l'analyse des performances, mais il produit également un graphique des appels pour vos applications, afin que vous puissiez voir quelles fonctions sont appelées, ainsi que leurs appelants.C'est évidemment complémentaire aux méthodes statiques mentionnées ailleurs sur la page, et cela ne sera utile que pour éliminer les classes, méthodes et fonctions totalement inutilisées - cela n'aidera pas à trouver du code mort à l'intérieur des méthodes qui sont réellement appelées.
la source
Je n'ai vraiment utilisé aucun outil qui fasse une telle chose ... Mais, pour autant que je l'ai vu dans toutes les réponses, personne n'a jamais dit que ce problème n'était pas calculable.
Qu'est-ce que je veux dire par là? Que ce problème ne peut être résolu par aucun algorithme sur un ordinateur. Ce théorème (qu'un tel algorithme n'existe pas) est un corollaire du problème de l'arrêt de Turing.
Tous les outils que vous utiliserez ne sont pas des algorithmes mais des heuristiques (c'est-à-dire pas des algorithmes exacts). Ils ne vous donneront pas exactement tout le code qui n'est pas utilisé.
la source
Une façon consiste à utiliser un débogueur et la fonction de compilation d'éliminer le code machine inutilisé lors de la compilation.
Une fois le code machine éliminé, le débogueur ne vous permet pas de mettre un breakpojnt sur la ligne correspondante de code source. Vous mettez donc des points d'arrêt partout et démarrez le programme et inspectez les points d'arrêt - ceux qui sont dans l'état "aucun code chargé pour cette source" correspondent au code éliminé - soit ce code n'est jamais appelé, soit il a été aligné et vous devez effectuer un minimum analyse pour trouver lequel de ces deux est arrivé.
C'est du moins ainsi que cela fonctionne dans Visual Studio et je suppose que d'autres ensembles d'outils peuvent également le faire.
C'est beaucoup de travail, mais je suppose que plus rapide que l'analyse manuelle de tout le code.
la source
CppDepend est un outil commercial qui peut détecter les types, méthodes et champs inutilisés, et faire bien plus. Il est disponible pour Windows et Linux (mais n'a actuellement pas de support 64 bits), et est livré avec un essai de 2 semaines.
Avertissement: je n'y travaille pas, mais je possède une licence pour cet outil (ainsi que NDepend , qui est une alternative plus puissante pour le code .NET).
Pour ceux qui sont curieux, voici un exemple de règle intégrée (personnalisable) pour détecter les méthodes mortes, écrite en CQLinq :
la source
Cela dépend de la plateforme que vous utilisez pour créer votre application.
Par exemple, si vous utilisez Visual Studio, vous pouvez utiliser un outil comme .NET ANTS Profiler qui est capable d'analyser et de profiler votre code. De cette façon, vous devez savoir rapidement quelle partie de votre code est réellement utilisée. Eclipse a également des plugins équivalents.
Sinon, si vous avez besoin de savoir quelle fonction de votre application est réellement utilisée par votre utilisateur final, et si vous pouvez publier votre application facilement, vous pouvez utiliser un fichier journal pour un audit.
Pour chaque fonction principale, vous pouvez suivre son utilisation, et après quelques jours / semaine, obtenez simplement ce fichier journal et jetez-y un œil.
la source
Je ne pense pas que cela puisse se faire automatiquement.
Même avec les outils de couverture de code, vous devez fournir suffisamment de données d'entrée pour exécuter.
Peut être un outil d'analyse statique très complexe et coûteux, comme celui du compilateur de Coverity ou LLVM, pourrait être utile.
Mais je ne suis pas sûr et je préférerais la révision manuelle du code.
ACTUALISÉ
Eh bien .. ne supprimant que les variables inutilisées, les fonctions inutilisées ne sont pas difficiles cependant.
ACTUALISÉ
Après avoir lu d'autres réponses et commentaires, je suis plus fermement convaincu que cela ne peut pas être fait.
Vous devez connaître le code pour avoir une mesure de couverture de code significative, et si vous savez qu'une grande partie de l'édition manuelle sera plus rapide que de préparer / exécuter / revoir les résultats de couverture.
la source
Un ami m'a posé cette même question aujourd'hui, et j'ai regardé autour de certains développements Clang prometteurs, par exemple ASTMatcher s et Static Analyzer qui pourraient avoir une visibilité suffisante dans la suite de la compilation pour déterminer les sections de code mort, mais ensuite je trouvé ceci:
https://blog.flameeyes.eu/2008/01/today-how-to-identify-unused-exported-functions-and-variables
C'est à peu près une description complète de la façon d'utiliser quelques drapeaux GCC qui sont apparemment conçus dans le but d'identifier les symboles non référencés!
la source
Le problème général de l'appel d'une fonction est NP-Complete. Vous ne pouvez pas savoir à l'avance d'une manière générale si une fonction sera appelée car vous ne saurez pas si une machine Turing s'arrêtera jamais. Vous pouvez obtenir s'il y a un chemin (statique) qui va de main () à la fonction que vous avez écrite, mais cela ne vous garantit pas qu'il sera jamais appelé.
la source
Eh bien, si vous utilisez g ++, vous pouvez utiliser ce drapeau -Wunused
Selon la documentation:
http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html
Edit: Voici un autre drapeau utile -Wunreachable-code Selon la documentation:
la source