Ich habe alten C ++ - Code, aus dem ich nicht verwendeten Code entfernen soll. Das Problem ist, dass die Codebasis groß ist.
Wie kann ich herausfinden, welcher Code niemals aufgerufen / nie benutzt wird?
c++
optimization
dead-code
user63898
quelle
quelle
f()
sind und ein Aufruf zumf()
eindeutigen Auflösen in die erste Funktion nur durch Hinzufügen einer dritten Funktion mit dem Namenf()
"das" Schlimmste, was Sie tun können "Durch Hinzufügen dieser dritten Funktion wird der Aufruf mehrdeutig und verhindert, dass das Programm kompiliert wird. Würde gerne (= entsetzt sein) ein Gegenbeispiel sehen.Antworten:
Es gibt zwei Arten von nicht verwendetem Code:
Für die erste Art kann ein guter Compiler helfen:
-Wunused
(GCC, Clang ) sollte vor nicht verwendeten Variablen warnen. Der nicht verwendete Clang-Analysator wurde sogar erhöht, um vor Variablen zu warnen, die niemals gelesen werden (obwohl sie verwendet werden).-Wunreachable-code
(älteres GCC, 2010 entfernt ) sollte vor lokalen Blöcken warnen, auf die nie zugegriffen wird (dies geschieht bei frühen Rückgaben oder Bedingungen, die immer als wahr ausgewertet werden).catch
Blöcken zu warnen , da der Compiler im Allgemeinen nicht beweisen kann, dass keine Ausnahme ausgelöst wird.Für die zweite Art ist es viel schwieriger. Statisch erfordert es eine Analyse des gesamten Programms, und obwohl die Optimierung der Verbindungszeit tatsächlich toten Code entfernen kann, wurde das Programm in der Praxis zum Zeitpunkt seiner Ausführung so stark transformiert, dass es nahezu unmöglich ist, dem Benutzer aussagekräftige Informationen zu übermitteln.
Es gibt daher zwei Ansätze:
gcov
. Beachten Sie, dass bestimmte Flags während der Kompilierung übergeben werden sollten, damit es ordnungsgemäß funktioniert). Sie führen das Tool zur Codeabdeckung mit einer Reihe unterschiedlicher Eingaben aus (Ihre Unit-Tests oder Nicht-Regressionstests). Der tote Code befindet sich notwendigerweise im nicht erreichten Code ... und Sie können von hier aus beginnen.Wenn Sie sich sehr für das Thema interessieren und die Zeit und die Neigung haben, ein Werkzeug selbst zu erarbeiten, würde ich empfehlen, die Clang-Bibliotheken zu verwenden, um ein solches Werkzeug zu erstellen.
Da Clang den Code für Sie analysiert und eine Überlastungsauflösung durchführt, müssen Sie sich nicht mit den Regeln für C ++ - Sprachen befassen und können sich auf das jeweilige Problem konzentrieren.
Diese Art von Technik kann jedoch die nicht verwendeten virtuellen Überschreibungen nicht identifizieren, da sie von Code von Drittanbietern aufgerufen werden können, über den Sie nicht nachdenken können.
quelle
foo()
dass es als "aufgerufen" markiert wird, wenn es nur in erscheint,if (0) { foo(); }
wäre ein Bonus, erfordert aber zusätzliche Intelligenz.)Für den Fall nicht verwendeter ganzer Funktionen (und nicht verwendeter globaler Variablen) kann GCC tatsächlich den größten Teil der Arbeit für Sie erledigen, vorausgesetzt, Sie verwenden GCC und GNU ld.
Verwenden Sie beim Kompilieren der Quelle
-ffunction-sections
und-fdata-sections
und dann beim Verknüpfen die Verwendung-Wl,--gc-sections,--print-gc-sections
. Der Linker listet nun alle Funktionen auf, die entfernt werden konnten, weil sie nie aufgerufen wurden, und alle Globals, auf die nie verwiesen wurde.(Natürlich können Sie den
--print-gc-sections
Teil auch überspringen und den Linker die Funktionen stillschweigend entfernen lassen, aber sie in der Quelle behalten.)Hinweis: Hier werden nur nicht verwendete vollständige Funktionen gefunden, und es wird nichts gegen toten Code in Funktionen unternommen. Funktionen, die aus totem Code in Live-Funktionen aufgerufen werden, bleiben ebenfalls erhalten.
Einige C ++ - spezifische Funktionen verursachen ebenfalls Probleme, insbesondere:
In beiden Fällen etwas verwendet , durch eine virtuelle Funktion oder eine globale Variablen Konstruktor hat auch gehalten um werden.
Eine zusätzliche Einschränkung besteht darin, dass beim Erstellen einer gemeinsam genutzten Bibliothek die Standardeinstellungen in GCC alle Funktionen in der gemeinsam genutzten Bibliothek exportieren , sodass sie für den Linker "verwendet" werden. Um dies zu beheben, müssen Sie standardmäßig festlegen, dass Symbole ausgeblendet werden, anstatt zu exportieren (z. B.
-fvisibility=hidden
), und dann die exportierten Funktionen, die Sie exportieren möchten, explizit auswählen.quelle
Wenn Sie g ++ verwenden, können Sie dieses Flag verwenden
-Wunused
Laut Dokumentation:
http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html
Bearbeiten : Hier ist andere nützliche Flagge
-Wunreachable-code
Entsprechend Dokumentation:Update : Ich habe ein ähnliches Thema gefunden. Erkennung von totem Code in einem älteren C / C ++ - Projekt
quelle
-Wunused
warnt vor Variablen, die deklariert (oder auf einmal deklariert und definiert), aber tatsächlich nie verwendet werden. Übrigens ziemlich ärgerlich mit Scoped Guards: p Es gibt eine experimentelle Implementierung in Clang, die auch vor nichtflüchtigen Variablen warnt, die in Ted Kremenek geschrieben, aber nie von ihm gelesen werden (von Ted Kremenek).-Wunreachable-code
warnt vor Code innerhalb einer Funktion , die nicht erreicht werden kann, kann es Code , der sich nach einer seinthrow
oderreturn
Anweisung oder Code in einem Zweig, der nie genommen wird beispielsweise (die im Falle einer tautologischen Vergleiche passiert).Ich denke, Sie suchen nach einem Tool zur Codeabdeckung . Ein Tool zur Codeabdeckung analysiert Ihren Code während der Ausführung und informiert Sie darüber, welche Codezeilen wie oft ausgeführt wurden und welche nicht.
Sie könnten versuchen, diesem Open-Source-Code-Coverage-Tool eine Chance zu geben: TestCocoon - Code-Coverage-Tool für C / C ++ und C #.
quelle
void func()
in a.cpp haben, die in b.cpp verwendet wird. Wie kann der Compiler überprüfen, ob func () im Programm verwendet wird? Es ist Linker Job.Die wirkliche Antwort hier ist: Man kann nie wirklich sicher wissen.
Zumindest für nicht triviale Fälle können Sie nicht sicher sein, ob Sie alles bekommen haben. Beachten Sie Folgendes aus dem Wikipedia-Artikel über nicht erreichbaren Code :
Wie Wikipedia richtig bemerkt, kann ein cleverer Compiler möglicherweise so etwas fangen. Aber überlegen Sie sich eine Modifikation:
Wird der Compiler dies erfassen? Vielleicht. Dazu muss jedoch mehr getan werden, als nur
sqrt
einem konstanten Skalarwert zu begegnen. Es muss herausgefunden werden, dass(double)y
es sich immer um eine Ganzzahl handelt (einfach), und dann muss der mathematische Bereichsqrt
für die Menge der Ganzzahlen (schwer) verstanden werden. Ein sehr hoch entwickelter Compiler kann dies möglicherweise für diesqrt
Funktion oder für jede Funktion in math.h oder für jede Funktion mit fester Eingabe tun , deren Domäne er herausfinden kann. Dies wird sehr, sehr komplex und die Komplexität ist im Grunde unbegrenzt. Sie können Ihrem Compiler immer mehr Komplexitätsebenen hinzufügen, aber es gibt immer eine Möglichkeit, Code einzuschleusen, der für einen bestimmten Satz von Eingaben nicht erreichbar ist.Und dann gibt es die Eingabesätze, die einfach nie eingegeben werden. Eingaben, die im wirklichen Leben keinen Sinn ergeben oder von der Validierungslogik an anderer Stelle blockiert werden. Der Compiler kann davon nichts wissen.
Das Endergebnis davon ist, dass die von anderen erwähnten Softwaretools zwar äußerst nützlich sind, Sie jedoch nie sicher wissen werden, dass Sie alles abgefangen haben, es sei denn, Sie gehen den Code anschließend manuell durch. Selbst dann werden Sie nie sicher sein, dass Sie nichts verpasst haben.
Die einzige wirkliche Lösung, IMHO, besteht darin, so wachsam wie möglich zu sein, die Ihnen zur Verfügung stehende Automatisierung zu nutzen, wo Sie können umzugestalten und ständig nach Möglichkeiten zu suchen, Ihren Code zu verbessern. Natürlich ist es trotzdem eine gute Idee, das zu tun.
quelle
Ich habe es selbst nicht benutzt, aber cppcheck behauptet, nicht verwendete Funktionen zu finden. Es wird wahrscheinlich nicht das gesamte Problem lösen, aber es könnte ein Anfang sein.
quelle
cppcheck --enable=unusedFunction --language=c++ .
, um diese nicht verwendeten Funktionen zu finden.Sie können versuchen, PC-lint / FlexeLint von Gimple Software zu verwenden . Es behauptet zu
Ich habe es für statische Analysen verwendet und fand es sehr gut, aber ich muss zugeben, dass ich es nicht verwendet habe, um spezifisch toten Code zu finden.
quelle
Mein normaler Ansatz, unbenutzte Sachen zu finden, ist
watch "make 2>&1"
neigt dazu, den Trick unter Unix zu machen.Dies ist ein etwas langwieriger Prozess, der jedoch gute Ergebnisse liefert.
quelle
Markieren Sie so viele öffentliche Funktionen und Variablen wie privat oder geschützt, ohne einen Kompilierungsfehler zu verursachen. Versuchen Sie dabei, auch den Code umzugestalten. Indem Sie Funktionen privat und teilweise geschützt machen, haben Sie Ihren Suchbereich verkleinert, da private Funktionen nur von derselben Klasse aufgerufen werden können (es sei denn, es gibt dumme Makros oder andere Tricks, um die Zugriffsbeschränkung zu umgehen, und wenn dies der Fall ist, würde ich Ihnen empfehlen einen neuen Job finden). Es ist viel einfacher festzustellen, dass Sie keine private Funktion benötigen, da nur die Klasse, an der Sie gerade arbeiten, diese Funktion aufrufen kann. Diese Methode ist einfacher, wenn Ihre Codebasis kleine Klassen hat und lose gekoppelt ist. Wenn Ihre Codebasis keine kleinen Klassen oder eine sehr enge Kopplung aufweist, empfehle ich, diese zuerst zu bereinigen.
Als nächstes werden alle verbleibenden öffentlichen Funktionen markiert und ein Aufrufdiagramm erstellt, um die Beziehung zwischen den Klassen herauszufinden. Versuchen Sie anhand dieses Baums herauszufinden, welcher Teil des Zweigs beschnitten werden kann.
Der Vorteil dieser Methode besteht darin, dass Sie sie auf Modulbasis ausführen können, sodass Sie Ihre Unittest problemlos weitergeben können, ohne lange Zeit zu haben, wenn Sie eine fehlerhafte Codebasis haben.
quelle
Wenn Sie unter Linux arbeiten, sollten Sie sich
callgrind
ein C / C ++ - Programmanalysetool ansehen , das Teil dervalgrind
Suite ist und auch Tools enthält, die auf Speicherlecks und andere Speicherfehler prüfen (die Sie ebenfalls verwenden sollten). Es analysiert eine laufende Instanz Ihres Programms und erstellt Daten zu seinem Aufrufdiagramm sowie zu den Leistungskosten der Knoten im Aufrufdiagramm. Es wird normalerweise für die Leistungsanalyse verwendet, erstellt jedoch auch ein Anrufdiagramm für Ihre Anwendungen, sodass Sie sehen können, welche Funktionen aufgerufen werden und welche Aufrufer sie haben.Dies ist offensichtlich eine Ergänzung zu den statischen Methoden, die an anderer Stelle auf der Seite erwähnt werden, und es wird nur hilfreich sein, um völlig unbenutzte Klassen, Methoden und Funktionen zu eliminieren - es hilft auch nicht, toten Code in Methoden zu finden, die tatsächlich aufgerufen werden.
quelle
Ich habe wirklich kein Tool verwendet, das so etwas tut ... Aber soweit ich in allen Antworten gesehen habe, hat noch niemand gesagt, dass dieses Problem nicht berechenbar ist.
Was meine ich damit? Dass dieses Problem von keinem Algorithmus jemals auf einem Computer gelöst werden kann. Dieser Satz (dass ein solcher Algorithmus nicht existiert) ist eine Folge von Turings Halteproblem.
Alle Werkzeuge, die Sie verwenden, sind keine Algorithmen, sondern Heuristiken (dh keine exakten Algorithmen). Sie geben Ihnen nicht genau den gesamten Code, der nicht verwendet wird.
quelle
Eine Möglichkeit besteht darin, einen Debugger und die Compiler-Funktion zu verwenden, um nicht verwendeten Maschinencode während der Kompilierung zu entfernen.
Sobald ein Teil des Maschinencodes entfernt ist, können Sie mit dem Debugger keinen Breakpojnt mehr in die entsprechende Zeile des Quellcodes einfügen. Sie setzen also überall Haltepunkte und starten das Programm und überprüfen die Haltepunkte - diejenigen, die sich im Status "Für diese Quelle kein Code geladen" befinden, entsprechen eliminiertem Code - entweder wird dieser Code nie aufgerufen oder er wurde inline gesetzt, und Sie müssen ein Minimum ausführen Analyse, um herauszufinden, welche dieser beiden passiert ist.
Zumindest funktioniert das in Visual Studio so, und ich denke, andere Toolsets können das auch.
Das ist viel Arbeit, aber ich denke schneller als die manuelle Analyse des gesamten Codes.
quelle
CppDepend ist ein kommerzielles Tool, das nicht verwendete Typen, Methoden und Felder erkennen und vieles mehr kann. Es ist für Windows und Linux verfügbar (bietet derzeit jedoch keine 64-Bit-Unterstützung) und wird mit einer zweiwöchigen Testversion geliefert.
Haftungsausschluss: Ich arbeite dort nicht, besitze jedoch eine Lizenz für dieses Tool (sowie NDepend , eine leistungsstärkere Alternative für .NET-Code).
Für diejenigen, die neugierig sind, ist hier ein Beispiel einer integrierten (anpassbaren) Regel zum Erkennen toter Methoden, die in CQLinq geschrieben wurde :
quelle
Dies hängt von der Plattform ab, auf der Sie Ihre Anwendung erstellen.
Wenn Sie beispielsweise Visual Studio verwenden, können Sie ein Tool wie .NET ANTS Profiler verwenden, mit dem Sie Ihren Code analysieren und profilieren können. Auf diese Weise sollten Sie schnell wissen, welcher Teil Ihres Codes tatsächlich verwendet wird. Eclipse hat auch äquivalente Plugins.
Andernfalls können Sie eine Protokolldatei für eine Prüfung verwenden, wenn Sie wissen müssen, welche Funktion Ihrer Anwendung tatsächlich von Ihrem Endbenutzer verwendet wird, und wenn Sie Ihre Anwendung problemlos freigeben können.
Für jede Hauptfunktion können Sie ihre Verwendung verfolgen und nach einigen Tagen / Woche einfach diese Protokolldatei abrufen und sie sich ansehen.
quelle
Ich denke nicht, dass es automatisch gemacht werden kann.
Selbst mit Tools zur Codeabdeckung müssen Sie ausreichende Eingabedaten bereitstellen, um ausgeführt zu werden.
Möglicherweise ist ein sehr komplexes und hochpreisiges statisches Analysetool wie das des Coverity- oder LLVM-Compilers hilfreich.
Aber ich bin mir nicht sicher und würde eine manuelle Codeüberprüfung vorziehen.
AKTUALISIERT
Nun, nur nicht verwendete Variablen zu entfernen, nicht verwendete Funktionen sind jedoch nicht schwer.
AKTUALISIERT
Nachdem ich andere Antworten und Kommentare gelesen habe, bin ich stärker davon überzeugt, dass dies nicht möglich ist.
Sie müssen den Code kennen, um eine aussagekräftige Messung der Codeabdeckung zu erhalten, und wenn Sie wissen, dass viele manuelle Bearbeitungen schneller sind als das Vorbereiten / Ausführen / Überprüfen der Abdeckungsergebnisse.
quelle
Ich hatte heute einen Freund, der mir genau diese Frage stellte, und ich sah mich einige vielversprechende Clang-Entwicklungen an, z. B. ASTMatcher und den Static Analyzer , die während des Kompilierens möglicherweise eine ausreichende Sichtbarkeit im Geschehen haben, um die toten Codeabschnitte zu bestimmen, aber dann ich Ich habe es gefunden:
https://blog.flameeyes.eu/2008/01/today-how-to-identify-unused-exported-functions-and-variables
Es ist so ziemlich eine vollständige Beschreibung der Verwendung einiger GCC-Flags, die anscheinend dazu dienen, nicht referenzierte Symbole zu identifizieren!
quelle
Das allgemeine Problem, ob eine Funktion aufgerufen wird, ist NP-Complete. Sie können im Allgemeinen nicht im Voraus wissen, ob eine Funktion aufgerufen wird, da Sie nicht wissen, ob eine Turing-Maschine jemals anhalten wird. Sie können feststellen, ob es einen Pfad (statisch) gibt, der von main () zu der von Ihnen geschriebenen Funktion führt, aber das garantiert nicht, dass er jemals aufgerufen wird.
quelle
Nun, wenn Sie g ++ verwenden, können Sie dieses Flag -Wunused verwenden
Laut Dokumentation:
http://docs.freebsd.org/info/gcc/gcc.info.Warning_Options.html
Bearbeiten: Hier ist andere nützliche Flagge -Wunreachable-Code Gemäß Dokumentation:
quelle