【问题标题】:How do we account for exception throws in static code analysis?我们如何解释静态代码分析中的异常抛出?
【发布时间】:2017-12-08 00:02:32
【问题描述】:

我编写了一个实用程序来为 java 方法创建 CFG(控制流图),其节点是基本块而不是指令。

我不能将异常抛出视为 CFG 中的边缘。原因是:

  1. try 块中的每条指令都可能引发异常/错误,这些异常/错误可由任何嵌套的 try-catch 块处理。如果我们将异常抛出视为边缘,则处理路径的数量会急剧增加,CFG 中的节点数量也会增加。
  2. 我们需要先了解异常的继承层次结构,然后才能决定可以进行哪些跳转。

静态代码分析器如何解决这个问题?

我被困在这一点上。如果我必须继续,我应该怎么做?

编辑:在我的例子中,我可以将支持限制在那些可以指定抛出异常的位置和异常的用例。这解决了我的第二个问题。我仍然想知道通用静态代码分析器是如何管理这个的。

【问题讨论】:

标签: java decompiler


【解决方案1】:

这是我在Krakatau decompiler 中处理问题的方式:

我们需要先了解异常的继承层次结构 决定哪些跳跃是可能的。

Krakatau 要求任何引用类的类定义都可用,因此它知道继承层次结构。但是,如果我重头来过,我就不会这样做了。需要类定义使反编译器对用户来说难以操作,因为查找和添加依赖项是一个巨大的痛苦。如果您对分析不太精确感到满意,您实际上并不需要这个。相反,您可以假设所有异常都可以到达所有处理程序。在实践中,我希望它会产生几乎相同的结果。

try 块中的每条指令都可能引发异常 / 可以由任何嵌套的 try-catch 块处理的错误。如果 我们将异常抛出视为边缘,处理路径的数量 急剧增加,CFG 中的节点数量也会急剧增加。

Krakatau 确实包含异常作为 CFG 中的边缘,这会导致您发现的问题。为了减少边的数量,我假装只有某些指令可以抛出(方法调用、数组访问、除法等)。这在技术上并不正确,但它对现实世界的代码是正确的。我从来没有见过任何真正关心链接错误、Thread.Stop 等引发的异常的东西。不过,我后来确实添加了一个选项来禁用此行为。

无论如何,这对于大多数代码来说都足够好,但它有时会导致性能问题。特别是,具有大量字段访问或方法调用的非常大的方法会导致巨大的 CFG,从而使反编译非常缓慢。我尝试了一些技巧来优化它,但最终的解决方案是从基本块转移到扩展基本块。

扩展基本块类似于基本块,只是异常边缘是半隐式表示的,因此 CFG 会小得多。 EBB 由直线代码组成,除了异常边缘之外,中间没有入口点或出口点,并且块中的每条指令都由同一组异常处理程序覆盖。这样一来,不是每条指令都有一个异常边缘,而是每个块都有一个异常边缘,从而提高效率。

即使是具有数千个方法调用的 Java 方法,通常也只有几个 try/catch,因此只有几个 EBB。

【讨论】:

  • 再次感谢!前段时间我确实使用 Krakatau 来做我的测试套件,它非常广泛。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-14
  • 2013-08-08
  • 2020-04-17
  • 1970-01-01
  • 2021-02-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多