13

我正在编写 PHP Web 应用程序的一部分(将在高中错误发现比赛中使用),用户必须在给定的 Java 程序中找到错误。作为其中的一部分,当 Java 程序执行时,我们希望突出显示 Java 程序源代码已执行的行。为此,我们只需要已执行的源代码的行号,即代码路径(或者称为代码覆盖率?)。我们将使用行号突出显示源文件中的行。

我们将使用 PHP 的 shell-exec() 来执行 Java 程序和获取代码路径的工具(无论是什么)。获取代码路径行号的最简单方法是什么?

非常感谢!

这是一张描述我们想要的图片

在此处输入图像描述

4

5 回答 5

4

PHP 会解释代码,这意味着每次运行程序时它都会在源代码上运行。这样做的好处是在读取代码时会爆炸(这使得行号打印变得微不足道);但是,它在其他方面通常很昂贵,因为您无法进行深度优化(或进行任何运行前错误检查)。

Java 将其代码编译成称为“字节码”的 JVM 汇编语言。 这意味着正在运行的程序通常无法访问(甚至使用)源代码。也就是说,有技巧。已编译的 Java 类具有添加“额外数据”的能力,其中一个“额外数据元素”是行号表,它是一个索引,允许运行程序集的人在编译器记录时“查找”行号.

这通常可以正常工作,考虑到:编译器通常不会标记每条指令,源代码可能不可用,优化可能会使某些内部代码块无法以有助于指向输入代码文本的方式运行。

代码覆盖率工具如何“解决”这个问题,因为它们通常会在代码中(在程序集级别)插入大量命令,这些命令有效地充当日志语句,以允许工具确定实际遵循的代码路径. 然后通过行号表尽可能地将其映射回来,然后用于突出显示原始源文件中的行。

如果您想要分辨率更高的东西(可以处理行的哪一部分被执行的东西),那么您需要更深入地挖掘。最终,您甚至可以考虑编写自己的编译器(或编译器扩展)来存储您自己的自定义行号表,从而克服当前解决方案的缺点。

抛出异常(正如 Shiven 提到的)和解析行号之类的技巧确实有效;但是,它们会通过奇怪的异常处理来污染您的代码,这些异常处理确实不是异常的,只是为了“获取行号”。由于代码混乱和异常的运行时性能通常较差,我倾向于避免使用此类解决方案(但它们确实有效)。

无论如何,希望这能让您了解为什么它并不总是以与 PHP 完全相同的方式工作。

于 2012-06-01T16:20:47.810 回答
1

看看 Cobertura。它计算覆盖率和类似的东西,如果它还没有这样做,那么将收集到的行号添加到它应该相对容易。有一个非常骇人听闻的尝试来做到这一点,但这太慢了,你可能无法在生产中使用它https://bitbucket.org/jowu/myriapod/wiki/Home

于 2012-06-01T16:27:43.330 回答
0

尝试参考这个链接JVMDI

您可以尝试访问程序计数器的值,然后将其映射到 lineNumberTable 。或者我认为 JVMDI 有一种方法可以访问执行代码的行号。我不确定后者,请参阅上面的链接,希望对您有所帮助。

于 2012-09-03T10:32:47.313 回答
0

如果使用 -g 选项编译程序,执行 printStackTrace(),捕获跟踪输出并从那里提取行号,则可以获得行号。

于 2012-06-01T16:11:52.917 回答
0

我从来没有做过或见过这样的事情,但这似乎是一个有趣的问题。我的想法是使用java 调试器 (jdb)来运行代码,而不仅仅是java命令。

您可以逐行(通过stepjdb 中的命令)逐步执行代码,并且每次执行一行时都会吐出其行号。这需要 PHP 方面的一点帮助(它必须解析行号并执行下一步命令),但行号就在那里。这是一个非常基本的 java 程序的示例输出。

Java (TestClass.java)

public class TestClass {
   public static void main(String[] args) {
      System.out.println("foo");
      System.out.println("bar");
   }
}

jdb(运行后的jdb TestClass javac TestClass.java

Initializing jdb ...
> stop at TestClass:3
Deferring breakpoint TestClass:3.
It will be set after the class is loaded.
> run
run TestClass
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
> 
VM Started: Set deferred breakpoint TestClass:3

Breakpoint hit: "thread=main", TestClass.main(), line=3 bci=0
3          System.out.println("foo");

main[1] step
> foo

Step completed: "thread=main", TestClass.main(), line=4 bci=8
4          System.out.println("bar");

main[1] step
> bar

Step completed: "thread=main", TestClass.main(), line=5 bci=16
5        }

main[1] step
> 
The application exited
于 2012-06-01T16:28:42.397 回答