关于debug出现unknown source的原理和解决

最近发现在开发调试的时候,突然发现自己的log中出现unknown source,后来发现以前测试都是用eclipse的自带工具编译的,现在改成用ANT来build就没有。去网上找了一下资料,原来是javac引起的。
因为当通过javac编译时,默认相当于指定-g:source,lines,这样编译出来的class文件中会包含源代码和行号信息;而通过ant编译时,默认相当于指定-g:none,这样编译出来的class文件会比较小,但是不包含任何调试信息,所以出错的时候就会打印出上面的错误堆栈信息。
自然想到是修改ANT中的javac的debug的参数。

不过还是不行,打印出来的log还是有unknown source的问题。后来仔细去看了一下build下的class的中的class,发现并没有重新编译,把里面的class删除重新编译一次,run ok!!!

下面是看到一个关于unknown source出现的原理,在网上看到的[url]http://www.blogjava.net/landon/archive/2011/02/27/345265.html[/url]
[quote]
Unknown Source,顾名思义,就是未知的源文件。因为我们最终解释运行的是class文件,所以出现这个问题的原因很简单,就是class文件中没有源文件的相关调试信息。那为什么class文件会没有调试信息呢?答案更简单,当然是我们在用javac命令进行编译的时候没有指定调试信息呗。因为现在很多人都习惯用eclipse等一些现成的ide进行编写代码,所以很少人熟悉jdk自己的javac,java,jdb等一些命令的详细参数(jdk的一些命令和eclipse自带的一些命令可能不同)。哈哈,不过如果你经常在linux下玩java的话,命令肯定会非常熟悉。那么让我们看看javac的一些重要参数:
-g-Generate all debugging information, including local variables. By default, only line number and source file information is generated.在class文件中生成所有调试信息,包括局部变量的信息。默认的话,只写入源码的行号和源文件信息。
-g:none-Do not generate any debugging information.不生成任何调试信息。
-g:(lines,vars,source)-只生成部分调试信息(源码行号,变量,源文件信息)。那我们在分别介绍下lines,vars,source的含义。
lines:将源文件中的行号信息写到Class文件中,此属性用于在Class文件中生成方法字节码流偏移量和源代码行号之间的映射关系。如果我们不指定此属性的话,我们将在堆栈异常信息中看不到打印的行号。
vars:Local variable属性建立了方法的栈帧中局部变量部分内容与源代码中局部变量名称和描述符之间的映射关系。有了这个属性,调试时,我们才可以看到变量的值。
source:编译时指定了这个属性,会把源文件的属性信息如源文件名称写入class文件。
说了这么多,初学者可能会迷糊,为什么编译要指定这些调试信息呢?哈哈,如果编译不指定这些调试信息的话,你怎么调试呢?如果你不指定行号信息的话,你在ide中都无法插入断点。这些调试信息在我们调试程序的时候非常重要。不过这些编译选项通常在ide中如eclipse中早已默认了。有的人可能还不相信,打开eclipse,依次打开菜单选项:Window->Preferences->Java->Compiler,可以看到页面的下方有一个Classfile Generation,默认是四个选项都选的。
那这个Unknown Source到底是编译的时候没有指定哪一项呢?经过测试,我发现是javac编译的时候没有没有指定source选项,必定出Unknown Source这个问题。
PS1:linux下,很多人用ant进行javac任务编译,查看堆栈异常时也经常会遇到Unknown Source的问题。ant编译时,默认相当于指定-g:none,及不生成任何调试信息的。所以如果要看到日志分析中的源码和行号信息时,要更改build.xml中的dubug属性。
PS2:我觉得看看Log4j的日志操作类源码包会对这个理解更有帮助。

B.刚开始的代码引子中,为什么自己写的代码会有堆栈异常的代码行数显示,而jdk的类库(rt.jar-Runtime Java Archive)代码会出现Unknown Source?
答案很简单,因为我们直接用的是jdk直接编译好的class文件。而rt.jar源码编译打包的时候,是没有将调试信息放入class文件的。所以才会显示Unknown Source。其实,道理很简单,sun的类库正常的情况下肯定不会有bug的,之前肯定都是调试过很多遍的,所以没有必要再加入调试信息,你只负责用就行了。所以,出现Unknown Source很正常。
PS:其实,我觉得这和软件的开发版本差不多。版本一般都有dubug版本和release版本。debug版本就是包含调试信息的。不过正式发行后,肯定不包含调试信息的。因为如果包含调试信息的话,可能版本占用空间会很大,而且根本就无需调试信息。

C.如果我们非要对jdk的类库如rt.jar进行跟踪调试怎么办?
因为rt.jar编译打包的时候,是不包含调试信息的。如果你只是想看看调用的过程,你只需要在eclipse中rt.jar下的Source attachment指定jdk安装目录的src.zip即可。不过如果你想跟踪jdk类库的变量值的时候,这样就不行了。除非,只有一种办法,你重新编译一下src.zip,指定好编译参数,然后用新编译好的rt.jar覆盖掉原来的rt.jar。这样就完全ok了。

D.如果我们想debug其他没有源代码的class文件呢?
其实,也不难,利用jad等反编译工具编译出源码后,在进行调试。不过前提是该class文件有调试信息。

PS:在网上看到了一些打印堆栈异常信息的代码,发现有的竟然打印出了jdk源码的所在行数。如
at java.lang.Thread.dumpStack(Thread.java:1206)等。我觉得很好奇,原因可能是重新编译了jdk的源码或者可能用的不同的ide或者不同版本的jdk吧。这个尚需考证。如果有懂的童鞋,可以和我交流。
[/quote]

你可能感兴趣的:(开发遇到的问题)