2011-03-15 185 views
1

我们正在研究用于测试java类文件的JVMTI Java代理。它的一小部分(显然)是本地C++代码,但更大的部分是Java代码,它通过网络加载并从本地代理程序代码调用。 我们使用代码覆盖工具来收集测试覆盖的java部分,它做源代码的工具。重新初始化Java类

现在,当我们的代理启动时,一些类会被初始化,特别是启动一个Thread的java.lang.ref.Reference。我们的代理使用自定义Java代码来处理线程启动方法,该代码由代码覆盖率工具进行检测。 覆盖工具将静态初始化器的一些静态内部类放入我们的Java代理程序代码中,所以这将作为java.lang.ref.Reference初始化的结果而执行。

问题是,在这个时候(当java.lang.ref.Reference被初始化时),JVM的一些基本功能还没有到位。具体来说,代码覆盖工具初始值设定项要访问System.getProperty(String name),但System.props仍然为空,因此调用会导致NullPointerException。 这导致代码覆盖率工具的静态内部类未被初始化,该类处于状态initialization_error,结果为NoClassDefFoundError。每次跟进访问此类都会导致NoClassDefFoundError。

我现在的意图是忽略这个初始化初始化错误,并等到VM_Start,然后重新设置相关类的ClassState为“链接”。这样我希望JVM会在后续访问类时再次尝试初始化类。

有没有人有一个想法,如果这可以从JVMTI代理完成,并给我一些建议如何做到这一点?

回答

0

从您的描述中,我了解到您的代码过于紧密。你真正想要的是在你开始测试之前在之前有一个初始化的Java VM

因此,而不是与本地代码和代理搞乱,我建议的代码分成三类:

  • 在一个抽象基类中的Java代码
  • 延伸的基类的类和用本机C++代码实现抽象方法。理想情况下,除了本地方法声明外,此类不应包含任何Java代码。
  • 测试的模型类,它定义了与空体相同的方法(或只返回null)。

对于测试,实例化模型类。覆盖测试所需的方法,以便返回测试代码所需的模拟数据对象(有关模拟测试的一些示例,请参见mockito

或者,在测试用例中测试Java加载代码。在所有其他测试中,将类添加到类路径中,并像往常一样实例化它。

+0

我们正处于一个集成测试的场景,也就是说我们希望在“真实世界”情况下而不是在模拟中测试我们的仪器和仪器化代码。正如我在之前的评论中所说的,唯一的问题是代码覆盖工具,它过早地进入游戏。由于这个工具不在我们的控制之下,并且是封闭源,所以我没有可能在那里改变任何东西。 – Ron 2011-03-15 14:22:58

+0

PS:是的,你说得对,本机代码与虚拟机紧密耦合,但这就是我们想要的东西:我们需要尽可能深入地发现虚拟机中发生的任何事情。这不是任何最终用户应用程序,这是一个非常技术性的分析工具。 – Ron 2011-03-15 14:25:53

+0

尝试像JaCoCo或EclEmma这样的其他代码覆盖工具。 – 2011-03-16 14:24:49

0

VM_Start后的RetransformClasses将为参考生成一个新的类加载挂钩,允许您在安全的VM阶段执行您的插装......只需重新转换引用或getLoadedClasses并将它们全部重新转换即可。

+0

我的理解是否正确:您建议稍后再做一下仪器。由于各种原因,这是不可能的。我恐怕不能在这里详细讨论,因为我根本不知道他们,但我的同事确信仪器必须尽早发生。基本上我们**要在代理中执行这种行为,但代码覆盖的源代码实现的情况是一个特殊情况,会造成问题。通常,当时唯一执行的代码来自jdk,并且此代码知道早期状态。 – Ron 2011-03-15 14:20:55

+0

在JVM的初始阶段,特别是在Sun JVM上,你将会遇到很多困难,并调用你自己的java。我的分析器全部使用C语言,并等待JVM_Start,然后打开静态布尔分析。原始期间的依赖性和循环性问题非常重要。你对这个阶段运行的早期Java方法真的很感兴趣吗? – 2011-03-18 11:36:51