Comment savoir quelles parties du code ne sont jamais utilisées?

312

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é?

user63898
la source
4
Je pense qu'un langage de requête de code vous donnera une meilleure vue de votre projet dans son ensemble. Je ne suis pas sûr du monde c ++ mais il semble y avoir cppdepend.com (ce n'est pas gratuit), qui semble assez décent. Peut-être que quelque chose comme ça peut être disponible gratuitement. L'autre chose est, avant de faire n'importe quel genre de refactoring, la chose sensée à faire serait d'avoir des tests unitaires si vous ne les avez pas en ce moment. Avec des tests unitaires, que pouvez-vous faire, c'est que vos outils de couverture de code profilent votre code qui, en soi, aidera à supprimer le code mort si vous ne pouvez pas couvrir ce code.
Biswanath
3
Consultez la référence ici: en.wikipedia.org/wiki/Unreachable_code
Martin York
6
Je trouve un sujet similaire. stackoverflow.com/questions/229069/…
UmmaGumma
3
Oui, une des choses amusantes de C ++ est que la suppression des fonctions "inutilisées" peut toujours altérer le résultat d'un programme.
MSalters
1
@MSalters: C'est intéressant ... Pour que ce soit le cas, il faudrait parler de la fonction dans un ensemble de surcharge qui est choisie pour un appel donné, n'est-ce pas? À ma connaissance, s'il y a 2 fonctions à la fois nommées 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ée f()- 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.
j_random_hacker

Réponses:

197

Il existe deux variétés de code inutilisé:

  • le local, c'est-à-dire que dans certaines fonctions, certains chemins ou variables ne sont pas utilisés (ou utilisés mais sans aucun sens, comme écrit mais jamais lu)
  • le global: des fonctions qui ne sont jamais appelées, des objets globaux qui ne sont jamais accessibles

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)
  • je ne connais aucune option pour avertir des catchblocs 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:

  • La théorie consiste à utiliser un analyseur statique. Un logiciel qui examinera le code entier à la fois dans les moindres détails et trouvera tous les chemins de flux. En pratique, je n'en connais aucun qui fonctionnerait ici.
  • La pragmatique est d'utiliser une heuristique: utilisez un outil de couverture de code (dans la chaîne GNU c'est 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.

  1. Utilisez la bibliothèque Clang pour obtenir un AST (arbre de syntaxe abstraite)
  2. Effectuer une analyse de marquage et de balayage à partir des points d'entrée

É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.

Matthieu M.
la source
7
Très sympa, +1. J'aime que vous distinguiez le code qui peut être statiquement déterminé de ne jamais s'exécuter en aucune circonstance et le code qui ne s'exécute pas dans une exécution particulière, mais qui pourrait potentiellement. Le premier est important, je pense, et comme vous le dites, une analyse d'accessibilité utilisant l'AST de l'ensemble du programme est le moyen de l'obtenir. (Empêcher foo()d'être marqué comme "appelé" alors qu'il n'apparaît qu'en if (0) { foo(); }serait un bonus mais nécessiterait des astuces supplémentaires.)
j_random_hacker
@j_random_hacker: peut-être que l'utilisation du CFG (Control-Flow Graph) serait mieux maintenant que j'y pense (grâce à votre exemple). Je sais que Clang tient à faire des remarques sur les comparaisons tautologiques comme celle que vous avez mentionnée et donc en utilisant le CFG, nous pourrions éventuellement repérer le code mort tôt.
Matthieu M.
@Matthieu: Ouais peut-être CFG est ce que je veux dire aussi, au lieu de AST :) Ce que je veux dire est: un digraphe dans lequel les sommets sont des fonctions et il y a un bord de la fonction x à la fonction y chaque fois que x pourrait éventuellement appeler y. (Et avec la propriété importante que les fonctions surchargées sont toutes représentées par des sommets distincts - on dirait que Clang le fait pour vous, ouf!)
j_random_hacker
1
@j_random_hacker: en fait, le CFG est plus compliqué qu'un simple digraphe, car il représente tout le code à exécuter en blocs avec des liens d'un bloc à un autre basés sur des instructions conditionnelles. Le principal avantage est qu'il est naturellement adapté à l'élagage du code qui peut être statiquement déterminé comme mort (il crée des blocs inaccessibles qui peuvent être identifiés), il serait donc préférable d'exploiter le CFG que l'AST pour construire le digraphe que vous êtes on parle de ... je pense :)
Matthieu M.
1
@j_random_hacker: en fait l'AST de Clang le fait, il rend tout explicite (ou presque ...) car il est destiné à travailler avec le code, pas seulement à le compiler. Il y a en fait une discussion en ce moment car apparemment il y a un problème avec les listes d'initialisation où une telle conversion implicite n'apparaît pas dans l'AST, mais je suppose que ce sera corrigé.
Matthieu M.
35

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-sectionset -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-sectionspartie 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:

  • Fonctions virtuelles. Sans savoir quelles sous-classes existent et lesquelles sont réellement instanciées au moment de l'exécution, vous ne pouvez pas savoir quelles fonctions virtuelles vous devez exister dans le programme final. L'éditeur de liens n'a pas suffisamment d'informations à ce sujet, il devra donc tous les garder.
  • Globales avec les constructeurs et leurs constructeurs. En général, l'éditeur de liens ne peut pas savoir que le constructeur d'un global n'a pas d'effets secondaires, il doit donc l'exécuter. De toute évidence, cela signifie que le mondial lui-même doit également être conservé.

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.

olsner
la source
Grands conseils pratiques. Le simple fait d'obtenir une liste des fonctions dont on sait qu'elles ne sont utilisées nulle part (même si, comme vous le dites, cette liste n'est pas complète), je pense que cela donnera beaucoup de fruits.
j_random_hacker
Je ne pense pas que tout cela fonctionne pour les modèles non justifiés .
Jakub Klinkovský
25

Eh bien, si vous utilisez g ++, vous pouvez utiliser ce drapeau -Wunused

Selon la documentation:

Avertit chaque fois qu'une variable est inutilisée en dehors de sa déclaration, chaque fois qu'une fonction est déclarée statique mais jamais définie, chaque fois qu'une étiquette est déclarée mais non utilisée, et chaque fois qu'une instruction calcule un résultat qui n'est explicitement pas utilisé.

http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html

Edit : Voici un autre indicateur utile -Wunreachable-code Selon la documentation:

Cette option est destinée à avertir lorsque le compilateur détecte qu'au moins une ligne entière de code source ne sera jamais exécutée, car une condition n'est jamais satisfaite ou parce qu'elle se trouve après une procédure qui ne revient jamais.

Mise à jour : j'ai trouvé un sujet similaire Détection de code mort dans un projet C / C ++ hérité

UmmaGumma
la source
4
Cela n'attrapera pas les en-têtes qui protègent des fonctions qui ne sont jamais appelées. Ou des méthodes de classe publique qui ne sont pas appelées. Il ne peut vérifier que si des variables de portée locale sont utilisées dans cette étendue.
Falmarri
@Falmarri Je n'ai jamais utilisé ce drapeau. J'essaie de comprendre moi-même quels types de codes morts je peux trouver avec.
UmmaGumma
-Wunusedmet 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-codemet en garde contre le code dans une fonction qui ne peut être atteint, il peut être code situé après une throwou returndéclaration ou un code dans une branche qui est jamais pris ( ce qui arrive en cas de comparaisons tautologiques) par exemple.
Matthieu M.
18

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 #.

Carlos V
la source
7
La clé ici est "pendant qu'elle s'exécute" - si vos données d'entrée n'exercent pas un chemin de code, ce chemin ne sera pas reconnu comme utilisé, n'est-ce pas?
sharptooth
1
C'est exact. Sans exécuter le code, il n'y a aucun moyen de savoir quelles lignes ne sont pas atteintes. Je me demande combien il sera difficile de mettre en place des tests unitaires pour émuler quelques exécutions normales.
Carlos V
1
@drhishch Je pense que la plupart de ces codes inutilisés doivent trouver l'éditeur de liens et non le compilateur.
UmmaGumma
1
@drhirsch Certes, le compilateur peut prendre en charge une partie du code inaccessible, comme les fonctions déclarées mais non appelées et certaines évaluations de court-circuit, mais qu'en est-il du code qui dépend de l'action de l'utilisateur ou des variables de temps d'exécution?
Carlos V
1
@golcarcol Ok, nous allons avoir une fonction 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.
UmmaGumma
15

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 :

double x = sqrt(2);
if (x > 5)
{
  doStuff();
}

Comme Wikipedia le note correctement, un compilateur intelligent peut être capable d'attraper quelque chose comme ça. Mais considérons une modification:

int y;
cin >> y;
double x = sqrt((double)y);

if (x != 0 && x < 1)
{
  doStuff();
}

Le compilateur comprendra-t-il cela? Peut être. Mais pour ce faire, il faudra faire plus que courir sqrtcontre une valeur scalaire constante. Il devra comprendre que ce (double)ysera toujours un entier (facile), puis comprendre la plage mathématique de sqrtl'ensemble des entiers (dur). Un compilateur très sophistiqué pourrait être en mesure de le faire pour la sqrtfonction, 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.

Justin Morgan
la source
1
Vrai et ne laissez pas de code mort! Si vous supprimez une fonctionnalité, tuez le code mort. Le laisser là "juste au cas où" cause juste un ballonnement qui (comme vous l'avez discuté) est difficile à trouver plus tard. Laissez le contrôle de version faire la thésaurisation pour vous.
Courses de légèreté en orbite
12

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.

Mr Shark
la source
Oui, il est capable de trouver des variables et des fonctions locales non référencées.
Chugaister
Oui, utilisez cppcheck --enable=unusedFunction --language=c++ .pour trouver ces fonctions inutilisées.
Jason Harris
9

Vous pouvez essayer d'utiliser PC-lint / FlexeLint de Gimple Software . Il prétend

rechercher des macros, typedef, classes, membres, déclarations, etc. inutilisés sur l'ensemble du projet

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.

Tony
la source
5

Mon approche normale pour trouver des trucs inutilisés est

  1. assurez-vous que le système de génération gère correctement le suivi des dépendances
  2. configurer un deuxième moniteur, avec une fenêtre de terminal plein écran, exécutant des builds répétés et affichant le premier écran de sortie. watch "make 2>&1"a tendance à faire l'affaire sur Unix.
  3. exécuter une opération de recherche et de remplacement sur l'arborescence source entière, en ajoutant "//?" au début de chaque ligne
  4. corrige la première erreur signalée par le compilateur, en supprimant le "//?" dans les lignes correspondantes.
  5. Répétez jusqu'à ce qu'il ne reste aucune erreur.

C'est un processus assez long, mais qui donne de bons résultats.

Simon Richter
la source
2
A du mérite, mais nécessite beaucoup de travail. Vous devez également vous assurer de décommenter toutes les surcharges d'une fonction en même temps - s'il y en a plusieurs applicables, la décommentation d'une fonction moins préférée peut permettre à la compilation de réussir mais entraîner un comportement incorrect du programme (et une idée incorrecte de laquelle sont utilisées).
j_random_hacker
Je ne commente que les déclarations dans la première étape (toutes les surcharges), et dans l'itération suivante, je vois les définitions manquantes; de cette façon, je peux voir quelles surcharges sont réellement utilisées.
Simon Richter du
@Simon: Fait intéressant dans un commentaire sur la question principale, MSalters souligne que même la présence / absence d'une déclaration pour une fonction qui n'est jamais appelée peut affecter laquelle des 2 autres fonctions est trouvée par la résolution de surcharge. Certes, cela nécessite une configuration extrêmement bizarre et artificielle, il est donc peu probable que cela soit un problème dans la pratique.
j_random_hacker
4

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.

Lie Ryan
la source
3

Si vous êtes sous Linux, vous voudrez peut-être callgrind un outil d'analyse de programme C / C ++ qui fait partie de la valgrindsuite, 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.

Adam Higuera
la source
3

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é.

geekazoid
la source
1
Je pense que l'OP veut principalement trouver des fonctions qui ne sont pas appelées de n'importe où, ce qui n'est certainement pas non calculable - la plupart des éditeurs de liens modernes peuvent le faire! C'est juste une question d'extraire cette information avec le moins de douleur et de corvée.
j_random_hacker
Vous avez raison, je n'ai pas vu le dernier commentaire de la question principale. Par ailleurs, il peut y avoir des fonctions référencées dans le code qui ne sont pas réellement utilisées. Ce genre de choses peut ne pas être détecté.
geekazoid
2

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.

acéré
la source
4
Je pense que la question de l'OP est de savoir comment trouver un sous-ensemble de code source plus petit et plus gérable, pas tellement en s'assurant que le binaire compilé est efficace.
j_random_hacker
@j_random_hacker Je lui ai donné une idée - et il s'avère que l'élimination du code peut même être utilisée pour retrouver le code source d'origine.
sharptooth
avez-vous des indicateurs de compilation spécifiques sur Visual Studio pour y parvenir? et ça marche seulement en mode release ou ça marche aussi en debug?
Naveen
Qu'en est-il des lignes utilisées mais optimisées par le compilateur?
Itamar Katz
@Naveen: Dans Visual C ++ 9, vous devez activer l'optimisation et utiliser / OPT: ICF
sharptooth
2

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 :

// <Name>Potentially dead Methods</Name>
warnif count > 0
// Filter procedure for methods that should'nt be considered as dead
let canMethodBeConsideredAsDeadProc = new Func<IMethod, bool>(
    m => !m.IsPublic &&       // Public methods might be used by client applications of your Projects.
         !m.IsEntryPoint &&            // Main() method is not used by-design.
         !m.IsClassConstructor &&      
         !m.IsVirtual &&               // Only check for non virtual method that are not seen as used in IL.
         !(m.IsConstructor &&          // Don't take account of protected ctor that might be call by a derived ctors.
           m.IsProtected) &&
         !m.IsGeneratedByCompiler
)

// Get methods unused
let methodsUnused = 
   from m in JustMyCode.Methods where 
   m.NbMethodsCallingMe == 0 && 
   canMethodBeConsideredAsDeadProc(m)
   select m

// Dead methods = methods used only by unused methods (recursive)
let deadMethodsMetric = methodsUnused.FillIterative(
   methods => // Unique loop, just to let a chance to build the hashset.
              from o in new[] { new object() }
              // Use a hashet to make Intersect calls much faster!
              let hashset = methods.ToHashSet()
              from m in codeBase.Application.Methods.UsedByAny(methods).Except(methods)
              where canMethodBeConsideredAsDeadProc(m) &&
                    // Select methods called only by methods already considered as dead
                    hashset.Intersect(m.MethodsCallingMe).Count() == m.NbMethodsCallingMe
              select m)

from m in JustMyCode.Methods.Intersect(deadMethodsMetric.DefinitionDomain)
select new { m, m.MethodsCallingMe, depth = deadMethodsMetric[m] }
Roman Boiko
la source
Mise à jour: la prise en charge 64 bits de Linux a été ajoutée dans la version 3.1.
Roman Boiko
1

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.

AUS
la source
1
.net ANTS Profiler ressemble à C # - êtes-vous sûr que cela fonctionne aussi pour C ++?
j_random_hacker
@j_random_hacker: aussi longtemps que je sache, cela fonctionne avec du code managé. Ainsi, .net ANTS ne sera certainement pas en mesure d'analyser le code C ++ «standard» (c'est-à-dire compilé avec gcc, ...).
AUS
0

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.

9dan
la source
2
le libellé de votre réponse est trompeur, LLVM n'a rien de cher ... c'est gratuit!
Matthieu M.
l'édition manuelle ne vous aidera pas avec les variables d'exécution qui passent par des branches logiques de creux dans votre programme. Et si votre code ne répond jamais à certains critères et suit donc toujours le même chemin?
Carlos V
0

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!

Steven Lu
la source
0

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é.

Luis Colorado
la source
-3

Eh bien, si vous utilisez g ++, vous pouvez utiliser ce drapeau -Wunused

Selon la documentation:

Warn whenever a variable is unused aside from its declaration, whenever a function is declared static but never defined, whenever a label is declared but not used, and whenever a statement computes a result that is explicitly not used.

http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html

Edit: Voici un autre drapeau utile -Wunreachable-code Selon la documentation:

This option is intended to warn when the compiler detects that at least a whole line of source code will never be executed, because some condition is never satisfied or because it is after a procedure that never returns.
ram singh
la source
6
Cette information exacte a déjà été mentionnée dans la réponse actuellement la mieux notée. Veuillez lire les réponses existantes pour éviter les doublons inutiles.
j_random_hacker
1
Vous pouvez maintenant gagner votre badge Peer Pressure!
Andrew Grimm