2013-04-05 8 views
7

Es gibt viele Zeiten, wenn beim Debuggen oder bei der Wiederverwendung von Code die Datei beginnt, Zeilen zu erhalten, die nichts tun, obwohl sie an einem Punkt etwas getan haben könnten.Entfernen von nutzlosen Zeilen aus C++ - Datei

Dinge wie Vektoren und immer gefüllt, und dann unbenutzt, Klassen/Strukturen, die definiert sind, aber nie verwendet werden, und Funktionen, die deklariert, aber nie verwendet werden.

Ich verstehe, dass in einigen Fällen einige dieser Dinge nicht überflüssig sind, wie sie aus anderen Dateien sichtbar sind, aber in meinem Fall gibt es keine anderen Dateien, nur irrelevanten Code in meiner Datei.

Während ich technisch verstehe, tut Aufruf von push_back etwas, und daher ist der Vektor per se nicht unbenutzt, in meinem Fall wird das Ergebnis ungenutzt.

Also: Gibt es eine Möglichkeit, dies zu tun, entweder mit einem Compiler (clang, gcc, VS, etc) oder einem externen Tool?

Beispiel:

#include<vector> 
using namespace std; 
void test() { 
    vector<int> a; 
    a.push_back(1); 
} 
int main() { 
    test(); 
    return 0; 
} 

Sollte sich: int main(){return 0};

Antwort

3

Unser DMS Software Reengineering Toolkit mit seinem C++ 11 Frontend könnte dazu verwendet werden; Derzeit macht es das nicht von der Stange. DMS wurde entwickelt, um benutzerdefinierte Werkzeugkonstruktionen für beliebige Quellsprachen bereitzustellen. Es enthält vollständige Parser, Namensauflöser und verschiedene Flussanalysatoren zur Unterstützung der Analyse sowie die Möglichkeit, Quellen-zu-Quell-Transformationen auf dem Code basierend auf Analyseergebnissen anzuwenden.

Im Allgemeinen möchten Sie eine statische Analyse, die bestimmt, ob jede Berechnung (Ergebnis, es kann mehrere sein, betrachten Sie nur "x ++") verwendet wird oder nicht. Für jede nicht verwendete Berechnung möchten Sie die nicht verwendete Berechnung entfernen und die Analyse wiederholen.Aus Effizienzgründen möchten Sie eine Analyse durchführen, die alle (Punkte der) Verwendung der Ergebnisse nur einmal bestimmt; Dies ist im Wesentlichen eine Datenflussanalyse. Wenn die Verwendungsmenge eines Berechnungsergebnisses leer ist, kann dieses Berechnungsergebnis gelöscht werden (beachten Sie, dass das Löschen des "x ++" -Werteergebnisses "x ++" verlassen kann, da das Inkrement immer noch benötigt wird!) Und die Verwendungsmengen von Berechnungen, von denen es abhängt kann angepasst werden, um Referenzen von der gelöschten zu entfernen, wodurch möglicherweise mehr Entfernungen verursacht werden.

Um diese Analyse für jede Sprache durchzuführen, müssen Sie in der Lage sein, Ergebnisse zu verfolgen. Für C (und C++) kann das ziemlich hässlich sein; Es gibt "offensichtliche" Verwendungen, bei denen ein Berechnungsergebnis in einem Ausdruck verwendet wird und wo es einer lokalen/globalen Variablen zugewiesen wird (die anderswo verwendet wird), und es gibt indirekte Zuweisungen durch Zeiger, Objektfeldaktualisierungen über beliebige Umwandlungen usw. Um diese Effekte zu kennen, muss Ihr Tool zur Analyse des toten Codes in der Lage sein, das gesamte Softwaresystem zu lesen und Datenflüsse darüber zu berechnen.

Um sicherzugehen, möchten Sie, dass diese Analyse konservativ ist, z. B. wenn das Werkzeug keinen Beweis dafür hat, dass ein Ergebnis nicht verwendet wird, dann muss davon ausgegangen werden, dass das Ergebnis verwendet wird; Sie müssen dies oft mit Zeigern (oder Array-Indizes, die nur verkappte Zeiger sind) tun, weil Sie im Allgemeinen nicht genau bestimmen können, wo ein Zeiger "zeigt". Man kann offensichtlich ein "sicheres" Werkzeug aufbauen, indem man annimmt, dass alle Ergebnisse verwendet werden: -} Sie werden auch mit manchmal sehr konservativen aber notwendigen Annahmen für Bibliotheksroutinen enden, für die Sie die Quelle nicht haben. In diesem Fall ist es hilfreich, einen Satz vorberechneter Zusammenfassungen der Nebeneffekte der Bibliothek zu haben (z. B. hat "strcmp" keine, "sprintf" überschreibt einen bestimmten Operanden, "push_back" ändert sein Objekt ...). Da Bibliotheken ziemlich groß sein können, kann diese Liste ziemlich groß sein.

DMS im Allgemeinen kann analysieren und gesamte Quellcode-Basis, Symboltabellen erstellen (also weiß, welche Bezeichner local/global und ihre genaue Art sind), Kontrolle und lokale Datenflussanalyse, erstellen Sie eine lokale "Nebenwirkungen" Zusammenfassung pro Funktion , erstellen Sie ein Call-Diagramm und globale Nebenwirkungen, und führen Sie eine globale Punkt-zu-Punkt-Analyse durch und stellen Sie diese "berechnete verwendete" Information mit entsprechendem Konservatismus zur Verfügung.

DMS wurde verwendet, um diese Berechnung auf C-Code-Systemen von 26 Millionen Zeilen Code (und ja, das ist eine sehr große Berechnung; es dauert 100 GB VM zu laufen). Wir haben den Dead Code Elimination Teil nicht implementiert (das Projekt hatte einen anderen Zweck), aber das ist einfach, sobald Sie diese Daten haben. DMS hat die Eliminierung des toten Codes bei großen Java-Codes mit einer konservativeren Analyse durchgeführt (z. B. "keine Verwendung eines Identifizierers", was bedeutet, dass Zuweisungen an den Identifizierer tot sind), was eine überraschende Menge an Codeentfernung in vielen realen Codes verursacht.

Der C++ - Parser von DMS erstellt derzeit Symboltabellen und kann eine Kontrollflussanalyse für C++ 98 durchführen, wobei C++ 11 in der Nähe ist. Wir benötigen noch eine lokale Datenflussanalyse, was ein wenig Aufwand ist, aber die globalen Analysen existieren bereits im DMS und stehen für diesen Effekt zur Verfügung. (Die "keine Verwendung eines Bezeichners" ist leicht aus den Symboltabellendaten verfügbar, wenn Sie eine konservativere Analyse nicht ablehnen).

In der Praxis möchten Sie nicht, dass das Tool nur die Daten stillschweigend abruft; einige könnten tatsächlich Berechnungen sein, die Sie trotzdem beibehalten möchten. Was das Java-Tool tut, ist zwei Ergebnisse zu produzieren: eine Liste der toten Berechnungen, die Sie inspizieren können, um zu entscheiden, ob Sie es glauben, und eine dead-Code entfernte Version des Quellcodes. Wenn Sie den Bericht über den toten Code glauben, behalten Sie die Version mit entferntem Code bei. Wenn Sie eine "tote" Berechnung sehen, von der Sie denken, dass sie nicht tot sein sollte, ändern Sie den Code so, dass er nicht tot ist, und führen Sie das Tool erneut aus. Mit einer großen Codebasis kann die Überprüfung des Berichts über den toten Code selbst ein Versuch sein; Woher weiß "you", ob ein gewisser, scheinbar toter Code nicht von "jemand anderem" in Ihrem Team bewertet wird? (Versionskontrolle kann verwendet werden, um wiederherzustellen, wenn Sie goof!)

Ein wirklich kniffliges Problem, das wir nicht (und kein Werkzeug, das ich kenne) behandeln, ist "toter Code" in Anwesenheit von bedingten Kompilierung.(Java hat dieses Problem nicht; C hat es in Pik, C++ Systeme viel weniger). Das kann wirklich böse sein. Stellen Sie sich eine Bedingung vor, in der Arm bestimmte Nebenwirkungen hat und der andere Arm verschiedene Nebenwirkungen hat, oder ein anderer Fall in dem der C++ - Compiler von GCC interpretiert wird und der andere Arm von MS interpretiert wird und die Compiler sich nicht einig sind, was die Konstrukte tun (Ja, die C++ - Compiler stimmen in dunklen Ecken nicht überein). Bestenfalls können wir hier sehr konservativ sein.

CLANG verfügt über eine gewisse Fähigkeit zur Durchflussanalyse; und eine gewisse Fähigkeit, Quelltransformationen durchzuführen, so dass es dazu gezwungen werden könnte. Ich weiß nicht, ob es eine globale Flow/Point-to-Analyse machen kann. Es scheint eine Tendenz zu einzelnen Kompilierungseinheiten zu haben, da es hauptsächlich eine Kompilierungseinheit kompiliert.

+0

[Interessantes Gespräch] (http://www.youtube.com/watch?v=C-_dw9iEzhA) auch – soandos

1

nicht verwendeten Variablen zu fangen, können Sie die -Wunused Flagge auf der gcc-Compiler aktivieren. Dies warnt Sie vor unbenutzten Parametern, Variablen und berechneten Werten zur Kompilierzeit. Ich habe festgestellt, dass die Verwendung der -Wall -Wextra und -Werror Flags sicherstellen, dass der Compiler einige der Probleme, die Sie beschreiben, abfängt. Weitere Informationen finden Sie hier: http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

Wie für nicht genutzte Klassen zu finden, ist eine Option ist eine IDE zu verwenden, wie Eclipse sagen, und verwenden Sie die ‚Referenzen suchen‘ -Funktion für Orte zu suchen, wo diese Klasse/Objekt sein kann benutzt.

+0

Das ist viel einfacher als das, was ich versuche – soandos

+0

Wenn ich es richtig verstanden habe, sucht er nicht nach unbenutzten Variablen, sondern nach Änderungen an Variablen, die nie wieder gelesen werden (dh die Variable wird wahrscheinlich gelesen/geschrieben viele Mal, aber es gibt eine Kette von Schreibvorgängen am Ende, die nie gelesen wird und er diese Schreibvorgänge eliminieren möchte) –

+0

@ DavidRodríguez-dribeas, siehe das Beispiel – soandos

1

Kurze Antwort ist "Nein." Es ist nicht möglich, durch statische Analyse des Client-Codes zu unterscheiden, dass die push_back-Methode des Vektors keine wichtigen Nebenwirkungen hat. Denn das Analyse-Tool weiß, dass es irgendwo in eine Datenbank schreibt und einen Aktienhandel betreibt.

+0

Ist das ein Problem, das man durch Annotieren von Funktionen lösen könnte? – soandos

+0

@soandos Das weiß ich nicht. Ich betone nur, dass dies eine viel tiefere Analyse erfordert als beispielsweise eine typische IDE. Und ich würde nicht den Atem anhalten. – djechlin

+0

+1. Normalerweise würde ich hier CppCheck vorschlagen. Fakt ist aber, dass im obigen Beispiel bei CppCheck nichts zu beanstanden ist. :-( – TobiMcNamobi

0

Ich würde empfehlen Versionssoftware - SVN, Git, Mercurial, Perforce, ... - zu verwenden, so dass Sie nach dem Debuggen das Versionsverwaltungstool verwenden können, um Debugging-Reste zu finden und zu entfernen. Dies macht es sehr einfach, Ihren Code schlanker zu halten.

Außerdem hat diese Art von Testcode normalerweise nur eine geringe Testabdeckung. Wenn Sie Unit-Tests durchführen, sollten sie als nicht abgedeckten Code angezeigt werden.

Dann gibt es Tools, die explizit nach dieser Art von Zeug suchen - Lint, Coverity und so weiter. Die meisten sind jedoch kommerziell. Versuchen Sie auch -O3 auf GCC zu verwenden, der Compiler erkennt möglicherweise mehr tatsächlich nicht verwendete Variablen auf diese Weise, da er den Code aggressiver inline und eliminiert.