Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2154387
  • 博文数量: 1647
  • 博客积分: 80000
  • 博客等级: 元帅
  • 技术积分: 9980
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-13 15:15
文章分类

全部博文(1647)

文章存档

2011年(1)

2008年(1646)

我的朋友

分类:

2008-10-28 18:17:18

汇总结果

    在获得所需的关于捕获和抛出异常的信息后,最后一步是汇总这些信息。在超类 visit(Code) 的调用返回后,将完全填充 throwListcaughtList 集合。它们包含关于方法中所有 try-catch 块的信息,所以您必须将抛出信息和捕获信息关联,以标识 bug 模式。

    清单 6 显示了用于标识 RuntimeException 捕获的逻辑。它将迭代捕获块的列表,如果发现捕获 Exception 的块,它会再次查找捕获块,该捕获块将捕获字节码同一范围的 RuntimeException。它还可以查找在字节码的相应范围中抛出 Exception 的实例。如果没有捕获 RuntimeException,也没有抛出 Exception,则存在一个潜在的 bug。



for (ExceptionCaught caughtException : catchList) {
    Set thrownSet = new HashSet();
    for (ExceptionThrown thrownException : throwList) {
        if (thrownException.offset >= caughtException.startOffset
                && thrownException.offset < caughtException.endOffset) {
            thrownSet.add(thrownException.exceptionClass);
  if (thrownException.exceptionClass.equals(caughtException.exceptionClass))
                caughtException.seen = true;
        }
    }
    int catchClauses = 0;
    if (caughtException.exceptionClass.equals("java.lang.Exception") 
      && !caughtException.seen) {
        // Now we have a case where Exception is caught, but not thrown
        boolean rteCaught = false;
        for (ExceptionCaught otherException : catchList) {
            if (otherException.startOffset == caughtException.startOffset
           && otherException.endOffset == caughtException.endOffset) {
                catchClauses++;
    if (otherException.exceptionClass.equals("java.lang.RuntimeException"))
                    rteCaught = true;
            }
        }
        int range = caughtException.endOffset - caughtException.startOffset;
        if (!rteCaught) {
      bugReporter.reportBug(new BugInstance(this, "REC_CATCH_EXCEPTION",
                    NORM_PRIORITY)
                    .addClassAndMethod(this)
                    .addSourceLine(this, caughtException.sourcePC));
        }
    }
}
      

   要编写 bug 检测器,则需要了解 JVM 字节码和类文件的一些结构。BCEL 和 FindBugs 库将为您处理此任务,并从字节码中提取信息,在稍高级别上呈现它。遗憾的是,关于 BCEL 和 FindBugs 如何支持类分离的文档并不能满足您的需要。像使用许多开放源码项目一样,关于如何编写检测器的最佳信息源是参照执行类似任务的其他检测器。

    使用静态分析的最大消耗是处理假警报。静态分析不一定精确,其目标不是发现 bug,而是只发现那些可能是 bug 的构造,这意味着有时会标记正确的代码出错。如果代码审核工具产生 95% 的假警报,那么任何人都不太可能想再次使用它;第一次发现报告新 bug 的假警报真的很痛苦。所以对于一个有效的 bug 模式检测器,它必须最小化假警报数量,最好使假情报不超过 50%。

     优化检测器的最佳方法是在 JDK 类库 (rt.jar)、Eclipse 或 JBoss 之类的大型代码基址上运行它。所以在编写 bug 检测器后,应该试着在新的项目和示例上运行它,以查看它们是真实的 bug,还是假警报。对于非凡的检测器(比如这里开发的检测器),第一次体验常常有点让人失望—— 假警报比预期的多。

优化检测器的过程包括查找假警报和细化 bug 模式,以消除某些假警报,同时不要将太多的真实 bug 排除在外。为细化模式,可以执行的操作之一是消除以下情况:存在零或 try 块中抛出已经过检查的异常;在这些情况下,捕获 Exception 不可能导致尝试合并多个捕获块,而会导致反映对捕获未经检查的异常的真实愿望。此修改对假警报率有很大的影响。

    优化检测器通常包括 “得分” 算法的使用,以确定是否将匹配报告为 bug。通过使用几个因素可执行其他调优,以增加或减少对给定实例的 “信心得分”。某些方面(如不存在任何已检查的异常)可以减少候选匹配的得分;其他方面,比如捕获异常失效方面(在捕获块中不使用),可以增加候选匹配的得分。清单 7 显示了对此检测器进行优化后形成的得分算法;它将优先级用作得分,因为在某一阈值上具有优先权的 bug 被 bug 报告的框架忽略(较高优先级的值指示的实际 bug 的严重性较低)。



if (!rteCaught) {
    int priority = LOW_PRIORITY + 1;
    if (range > 300) priority--;
    else if (range < 30) priority++;
    if (catchClauses > 1) priority++;
    if (thrownSet.size() > 1) priority--;
    if (caughtException.dead) priority--;
    bugReporter.reportBug(new BugInstance(this, "REC_CATCH_EXCEPTION",
            priority)
            .addClassAndMethod(this)
            .addSourceLine(this, caughtException.sourcePC));
}
      

    为静态代码分析工具(如 FindBugs)编写自定义 bug 检测器可以显著提高代码质量,并且有许多乐趣。尽管编写和优化 bug 检测器非常困难(优化它们对确保其能够使用非常重要),用检测器捕获 bug 模式的信息要付出高昂的代价,但是,使用这些信息能够花费少量工作来扫描任何项目中的 bug 模式,从而使您对最愚蠢的 bug 查找方式感到惊讶。

 

    [3] 

【责编:Peng】

--------------------next---------------------

阅读(266) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~