2008-10-09 33 views
14

Tôi có một hệ thống kế thừa lớn để duy trì. Các codebase sử dụng các chủ đề trên khắp nơi và những chủ đề chia sẻ rất nhiều dữ liệu có thể thay đổi. Tôi biết, nghe có vẻ xấu. Dù sao, không trả lời "viết lại toàn bộ ứng dụng từ đầu" hoặc tôi sẽ bỏ phiếu bạn xuống :-) Tôi đã cố gắng chạy một số công cụ phân tích tĩnh trên codebase, nhưng không ai trong số đó dường như bắt trường hợp này xảy ra rất nhiều trong mã nguồn của chúng tôi: nhiều chủ đề đang đọc và viết các biến không được đánh dấu là dễ bay hơi hoặc đồng bộ. Thông thường, điều này xảy ra trên các biến kiểu "runFlag". Một ví dụ về điều này là trên trang Phiên bản Java hiệu quả phiên bản thứ 2 260:Công cụ để tìm các lỗi dữ liệu có thể chia sẻ được chia sẻ trong 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; 
    } 
} 

Ví dụ này không bao giờ kết thúc trên Windows/Linux với tham số khởi động "-server" cho Sun JVM. Vì vậy, có cách nào (bán tự động) để tìm ra những vấn đề này hay tôi phải dựa hoàn toàn vào các đánh giá mã?

Trả lời

1

FindBugs và các công cụ chuyên nghiệp dựa trên đó là hy vọng tốt nhất của bạn, nhưng không tính vào việc tìm kiếm tất cả các tai ương đồng thời trong mã của bạn.

Nếu mọi thứ có hình dạng xấu, thì bạn nên bổ sung công cụ với phân tích bởi một chuyên gia đồng thời java của con người.

Đây là vấn đề khó khăn vì độc quyền prooving tính chính xác của cơ sở mã hiện có, nhưng đã được sửa đổi có thể sẽ không thực tế - đặc biệt là khi sử dụng đồng thời.

+0

FindBugs không phát hiện thấy thông báo "StopThread" ở trên. – auramo

+0

@auramo: điều đó có thể đúng, nhưng nó kiểm tra một số điều kiện khác. Và như tôi đã nói, những công cụ này không thể dựa vào để hoàn thành. –

2

Phiên bản mới nhất của FindBugs sẽ cố gắng kiểm tra các trường được đánh dấu bằng chú thích @GuardedBy chỉ được truy cập trong mã bảo vệ thích hợp.

1

Mức độ phù hợp làm cho một số công cụ phân tích tĩnh và động có thể giúp ích.

http://www.coverity.com/

6

Chris Grindstaff đã viết một bài báo FindBugs, Part 2: Writing custom detectors trong đó ông mô tả làm thế nào để sử dụng BCEL thêm các quy tắc của riêng bạn. (BCEL không phải là thư viện bytecode duy nhất - nhưng nó là thư viện được sử dụng bởi FindBugs.)

Mã dưới đây phát ra bất kỳ trường hợp nào khi phương thức truy cập một phương thức hoặc trường tĩnh. Bạn có thể chạy nó trên bất kỳ loại nào thực hiện Runnable.

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(); 
    } 

} 

Mã này phát ra như sau:

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

Nó sẽ có thể để sau đó quét các loại StopThread, tìm lĩnh vực này và kiểm tra để xem nếu nó là dễ bay hơi.

Kiểm tra đồng bộ hóa là có thể, nhưng có thể gặp khó khăn do nhiều điều kiện MONITOREXIT. Đi bộ lên ngăn xếp cuộc gọi có thể khó khăn quá, nhưng sau đó đây không phải là một vấn đề tầm thường. Tuy nhiên, tôi nghĩ rằng nó sẽ tương đối dễ dàng để kiểm tra một mẫu lỗi nếu nó đã được thực hiện nhất quán.

BCEL trông có vẻ như được ghi lại và thực sự có lông cho đến khi bạn tìm thấy lớp học BCELifier. Nếu bạn chạy nó trên một lớp, nó sẽ rút ra nguồn Java về cách bạn sẽ xây dựng lớp đó trong BCEL như thế nào.Chạy nó trên StopThread cho này để tạo ra các truy cập $ 0 accessor tổng hợp:

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 Chủ đề Analyzer hiện công việc, nhưng điều đó là khá tốn kém. Công cụ phân tích thời gian chạy đa luồng của IBM cho Java dường như có khả năng phát hiện những công cụ này nhưng có vẻ khó thiết lập hơn. Đây là các công cụ phân tích động để phát hiện các biến thực được truy cập từ các luồng khác nhau mà không đồng bộ hóa hoặc biến động thích hợp, vì vậy kết quả chính xác hơn so với phân tích tĩnh và có thể tìm thấy nhiều vấn đề mà phân tích tĩnh không thể phát hiện.

Nếu mã của bạn hầu như được đồng bộ hóa hoặc đồng bộ hóa một cách chính xác, việc kiểm tra FindBugs (hoặc phân tích tĩnh khác) cũng có thể giúp bạn, ít nhất là các quy tắc IS2_INCONSISTENT_SYNC và UG_SYNC_SET_UNSYNC_GET có thể là bắt đầu.

Các vấn đề liên quan