2008-10-09 10 views
14

Ich habe ein großes Legacy-System zu pflegen. Die Codebasis verwendet Threads überall und diese Threads teilen viele veränderbare Daten. Ich weiß, klingt schlecht. Wie auch immer, antworten Sie nicht "schreiben Sie die ganze Anwendung von Grund auf neu" oder ich stimme Sie ab :-) Ich habe versucht, einige statische Analyse-Tools auf der Codebasis laufen, aber keiner von ihnen scheint diesen Fall zu fangen, der viel auftritt in unserem Quelltext: Mehrere Threads lesen und schreiben Variablen, die nicht als flüchtig oder überhaupt synchronisiert markiert sind. In der Regel tritt dies bei Variablen vom Typ "runFlag" auf. Ein Beispiel hierfür ist auf Effective Java 2. Auflage Seite 260:Tools zum Suchen freigegebener änderbarer Datenfehler in Java

public class StopThread 
{ 
    private static boolean stopRequested; 
    public static void main(String[] args) throws InterruptedException 
    { 
     Thread backgroundThread = new Thread(new Runnable() 
     { 
      public void run() 
      { 
       int i = 0; 
       while (!stopRequested) 
       { 
        i++; 
       } 
      } 
     }); 
     backgroundThread.start(); 
     Thread.sleep(1000); 
     stopRequested = true; 
    } 
} 

Dieses Beispiel endet nie auf Windows/Linux mit „-Server“ Startparameter auf Sun JVM gegeben. Gibt es also (halb-) automatische Wege, diese Probleme zu finden, oder muss ich mich vollständig auf Code Reviews verlassen?

Antwort

1

FindBugs und professionelle Tools, die darauf basieren, sind Ihre beste Hoffnung, aber zählen Sie nicht darauf, dass Sie alle Nebenläufigkeitsprobleme in Ihrem Code finden.

Wenn die Dinge in dieser schlechten Form sind, dann sollten Sie die Werkzeuge durch einen menschlichen Javakonkurrenten ergänzen.

Dies ist ein hartes Problem, weil abschließend die Korrektheit einer bestehenden, aber modifizierten Codebasis wird wahrscheinlich unrealistisch sein - vor allem angesichts der gleichzeitigen Verwendung.

+0

FindBugs erkennt das obige "StopThread" -Ausgabe nicht. – auramo

+0

@auramo: das könnte stimmen, aber es prüft auf eine Reihe anderer Bedingungen. Und wie gesagt, auf diese Tools kann man sich nicht vollständig verlassen. –

2

Die neueste Version von FindBugs versucht zu überprüfen, dass Felder mit der @GuardedBy Annotation nur innerhalb des entsprechenden Sicherheitscodes aufgerufen werden.

1

Coverity stellt einige statische und dynamische Analysewerkzeuge zur Verfügung, die hilfreich sein können.

http://www.coverity.com/

6

Chris Grindstaff schrieb einen Artikel FindBugs, Part 2: Writing custom detectors, in dem er beschreibt, wie die BCEL benutzen, um Ihre eigenen Regeln hinzuzufügen. (BCEL ist nicht die einzige Bytecode-Bibliothek - aber es ist die von FindBugs verwendete.)

Der folgende Code gibt Fälle aus, in denen eine Methode auf eine statische Methode oder ein Feld zugreift. Sie können es auf jedem Typ ausführen, der Runnable implementiert.

public class StaticInvocationFinder extends EmptyVisitor { 

    @Override 
    public void visitMethod(Method obj) { 
     System.out.println("=========================="); 
     System.out.println("method:" + obj.getName()); 

     Code code = obj.getCode(); 
     InstructionList instructions = new InstructionList(code.getCode()); 
     for (Instruction instruction : instructions.getInstructions()) { 
      // static field or method 
      if (Constants.INVOKESTATIC == instruction.getOpcode()) { 
       if (instruction instanceof InvokeInstruction) { 
        InvokeInstruction invokeInstruction = (InvokeInstruction) instruction; 
        ConstantPoolGen cpg = new ConstantPoolGen(obj 
          .getConstantPool()); 
        System.out.println("static access:" 
          + invokeInstruction.getMethodName(cpg)); 
        System.out.println("  on type:" 
          + invokeInstruction.getReferenceType(cpg)); 
       } 
      } 
     } 
     instructions.dispose(); 
    } 

    public static void main(String[] args) throws Exception { 
     JavaClass javaClass = Repository.lookupClass("StopThread$1"); 

     StaticInvocationFinder visitor = new StaticInvocationFinder(); 
     DescendingVisitor classWalker = new DescendingVisitor(javaClass, 
       visitor); 
     classWalker.visit(); 
    } 

} 

Dieser Code gibt das folgende:

========================== 
method:<init> 
========================== 
method:run 
static access:access$0 
     on type:StopThread 

Es wäre möglich, den Typ StopThread, finden Sie das Feld dann zu scannen und überprüfen, um zu sehen, ob es flüchtigen ist.

Die Überprüfung auf Synchronisierung ist möglich, kann jedoch aufgrund mehrerer MONITOREXIT-Bedingungen schwierig werden. Steigende Call-Stacks könnten auch schwierig sein, aber das ist kein triviales Problem. Ich denke jedoch, dass es relativ einfach wäre, nach einem Fehlermuster zu suchen, wenn es konsistent implementiert wurde.

BCEL sieht spärlich dokumentiert und wirklich haarig, bis Sie die BCELifier Klasse finden. Wenn Sie es für eine Klasse ausführen, spuckt es eine Java-Quelle aus, wie Sie die Klasse in BCEL erstellen würden.Laufen sie auf StopThread ergibt dies für die Erzeugung des Zugang $ 0 synthetischen Accessor:

private void createMethod_2() { 
    InstructionList il = new InstructionList(); 
    MethodGen method = new MethodGen(ACC_STATIC | ACC_SYNTHETIC, Type.BOOLEAN, Type.NO_ARGS, new String[] { }, "access$0", "StopThread", il, _cp); 

    InstructionHandle ih_0 = il.append(_factory.createFieldAccess("StopThread", "stopRequested", Type.BOOLEAN, Constants.GETSTATIC)); 
    il.append(_factory.createReturn(Type.INT)); 
    method.setMaxStack(); 
    method.setMaxLocals(); 
    _cg.addMethod(method.getMethod()); 
    il.dispose(); 
    } 
2

Coverity Thema Analyzer macht den Job, aber das ist ziemlich teuer. Das IBM Multi-Thread-Laufzeitanalyse-Tool für Java scheint diese zu erkennen, aber es scheint etwas schwieriger zu sein, es einzurichten. Dies sind dynamische Analysetools, die erkennen, auf welche tatsächlichen Variablen von verschiedenen Threads ohne ordnungsgemäße Synchronisation oder Volatilität zugegriffen wurde. Die Ergebnisse sind daher genauer als bei der statischen Analyse und können viele Probleme finden, die die statische Analyse nicht erkennen kann.

Wenn Ihr Code größtenteils oder zumindest teilweise richtig synchronisiert ist, könnte das Korrigieren von FindBugs (oder anderen statischen Analysen) auch helfen, zumindest die Regeln IS2_INCONSISTENT_SYNC und UG_SYNC_SET_UNSYNC_GET könnten zu Beginn gut sein.