2012-11-12 7 views
7

Das Legacy-Projekt, an dem ich gerade arbeite, enthält einige externe Bibliotheken in Form von binären JAR-Dateien. Wir haben beschlossen, dass wir für die Analyse und potentielles Patchen Quellen dieser Bibliothek erhalten wollen, sie verwenden, um neue Binärdateien zu erstellen, und nach ausführlichen und langwierigen Regressionstests zu diesen Binärdateien wechseln.So überprüfen Sie, ob Binärdateien aus bestimmten Quellen erstellt werden

Angenommen, wir haben die Quellen bereits abgerufen und gebaut (ich bin gerade in der Planungsphase). Vor dem eigentlichen Testen würde ich gerne einige "Kompatibilitätsüberprüfungen" durchführen, um die Möglichkeit auszuschließen, dass die Quellen etwas darstellen, das sich grundlegend von dem unterscheidet, was in den "alten" Binärdateien ist.

Mit dem javap Tool konnte ich die Version von JDK extrahieren, die für die Kompilierung verwendet wurde (zumindest glaube ich, dass es die Version von JDK ist). Es besagt, dass die Binärdateien mit der Hauptversion 46 und der Minor 0 erstellt wurden. Gemäß this article wird JDK 1.2 zugeordnet.

Angenommen, das gleiche JDK würde für die Quellenkompilierung verwendet werden.

Die Frage ist: Gibt es eine zuverlässige und möglicherweise wirksame Methode zur Überprüfung, ob diese beiden Binärdateien aus den gleichen Quellen gebaut werden? Ich würde gerne wissen, ob alle Methodensignaturen und Klassendefinitionen identisch sind und ob die meisten oder alle Methodenimplementierungen identisch/ähnlich sind.

Die Bibliothek ist ziemlich groß, daher denke ich, dass eine detaillierte Analyse von dekompilierten Binärdateien keine Option sein kann.

+0

Reflection ('java.lang.reflect') sollte für Klassen- und Methodensignaturen, aber nicht für die Umsetzung tun. – SJuan76

+0

Was ist mit dem Vergleich der MD5-Hashes der beiden Binärdateien? – sp00m

+1

Zukünftige Referenz, der einfachste Weg, um herauszufinden, vorwärts (es wird Ihnen jetzt nicht helfen) ist ein Versionskontrollsystem wie Git, Subversion oder Mercurial, dann die Revisionsnummer und/oder Changeset-ID in Ihrem verwenden jar, z. B. in der Manifestdatei. – Brian

Antwort

1

schlage ich einen mehrstufigen Prozess:

Nehmen die zuvor Jardiff oder ähnliches vorgeschlagen, wenn es zu sehen, irgendwelche API Unterschiede. Wählen Sie nach Möglichkeit ein Werkzeug mit einer Option zum Melden von privaten Methoden usw. In der Praxis wird jede wesentliche Änderung der Implementierung in Java wahrscheinlich einige Methoden und Klassen ändern, selbst wenn die öffentliche API unverändert ist.

Wenn Sie eine API-Übereinstimmung haben, kompilieren Sie einige zufällig ausgewählte Dateien mit dem angegebenen Compiler, dekompilieren Sie das Ergebnis und die ursprünglichen Klassendateien und vergleichen Sie die Ergebnisse. Wenn sie übereinstimmen, wenden Sie den gleichen Prozess auf immer größere Codes an, bis Sie entweder einen Konflikt gefunden haben oder alles überprüft haben.

Indizes von dekompiliertem Code geben eher Hinweise auf die Art der Unterschiede und sind leichter für nicht signifikante Unterschiede zu filtern als die eigentlichen Klassendateien.

Wenn Sie eine Abweichung erhalten, analysieren Sie sie. Vielleicht liegt es an etwas, das Ihnen egal ist. Wenn ja, versuchen Sie, ein Skript zu erstellen, das diese Form der Differenz löscht und den Kompilierungs- und Vergleichsprozess wieder aufnimmt. Wenn es zu weit verbreiteten Fehlanpassungen kommt, experimentieren Sie mit Compiler-Parametern wie der Optimierung. Wenn Anpassungen an den Compiler-Parametern die Unterschiede beseitigen, fahren Sie mit dem Massenvergleich fort. Das Ziel in dieser Phase besteht darin, eine Kombination aus Compiler-Parametern und dekompilierten Code-Filtern zu finden, die eine Übereinstimmung der Beispieldateien erzeugen und sie auf den Massenvergleich der Bibliothek anwenden.

Wenn Sie im dekompilierten Code keine vernünftige Übereinstimmung finden, haben Sie wahrscheinlich nicht den richtigen Quellcode. Wenn Sie jedoch eine API-Übereinstimmung haben, kann es sich lohnen, Ihr System aufzubauen und Ihre Tests mit dem Ergebnis der Kompilierung auszuführen. Wenn Ihre Tests mit der Version, die Sie aus der Quelle erstellt haben, mindestens genauso gut funktionieren, fahren Sie mit der Arbeit fort.

+0

Ich entschied mich, die meisten Ihrer Vorschläge zu verwenden. Vielen Dank :) –

0

Es gibt eine Vielzahl von JAR-Vergleichswerkzeugen. Eine, die früher ziemlich gut war, ist Jardiff. Ich habe es seit einiger Zeit nicht mehr benutzt, aber ich bin sicher, es ist noch verfügbar. Es gibt auch einige kommerzielle Angebote im gleichen Raum, die Ihren Bedürfnissen entsprechen könnten.

0

Jardiff, dass die Wahrnehmung erwähnt ist ein guter Anfang, aber es gibt keine Möglichkeit, es theoretisch zu 100% sicher zu machen. Dies liegt daran, dass dieselbe Quelle mit verschiedenen Compilern und unterschiedlichen Compilerkonfigurationen und Optimierungsebenen kompiliert werden kann. Es gibt also keine Möglichkeit, Binärcode (Bytecode) über Klassen- und Methodensignaturen hinaus zu vergleichen.

Was meinen Sie mit "ähnliche Implementierung" einer Methode? Nehmen wir an, dass ein cleverer Compiler einen else Fall fallen lässt, weil er herausfindet, dass die Bedingung möglicherweise niemals wahr ist. Sind die beiden ähnlich? Ja und Nein .. :-)

Der beste Weg zu gehen IMHO ist sehr gute Regression Testfälle, die jedes wichtige Merkmal Ihrer Bibliotheken überprüfen. Das könnte ein Horror sein, aber auf lange Sicht könnte es billiger sein, als nach Bugs zu suchen. Alles hängt von Ihren zukünftigen Plänen in diesem Projekt ab. Keine triviale einfache Entscheidung.

0

Verwenden Sie für Methodensignaturen ein Tool wie Jardiff.

Für die Ähnlichkeit der Implementierung müssen Sie auf eine wilde Vermutung zurückgreifen. Der Vergleich des Bytecodes auf Opcode-Ebene kann compilerabhängig sein und zu einer großen Anzahl falscher Negative führen. Wenn dies der Fall ist, können Sie die Methoden einer Klasse mithilfe der LineNumberTable vergleichen.

Es gibt Ihnen eine Liste von Zeilennummern für jede Methode (solange die Klassendatei mit dem Debug-Flag kompiliert wurde, das oft in sehr alten oder kommerziellen Bibliotheken fehlt).

Wenn zwei Klassendateien aus demselben Quellcode kompiliert werden, sollten mindestens die Zeilennummern jeder Methode exakt übereinstimmen.

Sie können eine Bibliothek wie Apache BCEL verwenden, um die LineNumberTable abzurufen:

// import org.apache.bcel.classfile.ClassParser; 
    JavaClass fooClazz = new ClassParser("Foo.class").parse(); 
    for(Method m : fooClazz.getMethods()) 
    { 
    LineNumberTable lnt = m.getLineNumberTable(); 
    LineNumber[] tab = lnt.getLineNumberTable(); 
    for(LineNumber ln : tab) 
    { 
     System.out.println(ln.getLineNumber()); 
    } 
    } 
Verwandte Themen