`
haoningabc
  • 浏览: 1466017 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

(转载)gdb调试java

    博客分类:
  • gdb
 
阅读更多
转载http://blog.csdn.net/haoel/article/details/2289305
http://gcc.gnu.org/java/gdb.html
http://www.linuxjournal.com/article/4860

用GDB 调试Java程序
 
陈皓
http://blog.csdn.net/haoel
 
背景
 
想要使用GDB调试程序,就需要用GNU的编译器编译程序。如:用GCC编译的C/C++的程序,才能用GDB调试。对于Java程序也是一样的,如果想要用GDB调试,那么就需要用GNU的Java编译器——GCJ来编译Java程序。
 
目前,很多Linux都不会预装Sun的JVM,取而代之是使用GNU的开源编译器来编译和运行Java程序。比如RedHat和Ubuntu,其默认安装都是使用GNU的Java编译器(gcj)和解释器(gij)。当然,它们都被脚本javac和java包装了起来,你一不小心还以为是使用了Sun的JVM。
 
为什么GNU要搞出一个Java的编译和解释器来呢?其大致有以下几点:
 
a)      传统的JVM太慢了,因为它解释的是class文件中的bytecode。这种方法实在是太慢了。

b)      为了优化性能,引入了JIT(Just-In-Time),JIT会分析代码,找出那些被反复调用到一定次数的方法和函数,然后直接把这个方法直接处理成汇编machine code,以后就直接运行机器码了。

c)      当然,JIT也有问题,一个是startup overhead,就是说启动的时候有点过分了,表现为时间慢,并且,每次编译后,都需要JIT重新做来过。另一个问题是JIT比较耗费空间。

d)      传统的java还有一个比较扯的问题,就是布署起来太麻烦了,需要有N个jar文件,而不是一个可执行文件。并且,Java需要一个很肥大的运行环境。另外,在java和c/c++之间的调用慢得令人受不了。
 
 
GNU的Java编译器GCJ
 
上述的东西是催生出现gcj的原因,GNU用了Ahead-of-Time Compilation来形容GCJ。GNU对GCJ的出现在理由做了下面的说明:
 
a)      GCC本来可以编译多种程序语言,所以,把java整进来也是一件make sense(合乎逻辑)的事情。
 
b)      Java的编译是一件非常简单的事情,因为没有C++的模板和预编译器,而且system type, object model 和 exception handling 也很简单。所以,这对于擅长编译技术的GNU来说,从编译方面优化Java的性能是一些很简单的事。
 
c)       gcj会对java程序做N多的优化工作,比如:common sub-expression elimination, strength reduction, loop optimization 和 register allocation。在优化方面,是GCJ牛还是JIT牛,存在一些较大的争论。对于JIT来说,它可以裁剪和做适时优化,因为是在运行时。 Sun的HotSpot技术是其中比较牛的技术,但gcj的技术也不一定就比JIT差。
 
d)      对于使用gcj的人来说,最大的一个好处就是startup speed和内存空间使用率。启动JVM或JIT会肖耗很大的内存,例如:NetBean启动就需要74M的内存(什么事也没有干), JEmacs使用Swing,一启动就是26M,而XEmacs只有8M(这些数据是比较老的了,大约在2003年的数据)。
 
e)       当GCJ刚出道时,有人比较了Kawa Test Suite在GCJ和JDK1.3.1下的运行比较。结果是,GCJ速度比Sun的JIT快两倍,因为GCJ比Sun的JDK少了一半以上的内存访问未命中的事情,也就是说少了一半的内存换页。并且,实际运行过程中,也少了25%的内存使用。
 
f)        最后,GCJ用的是一个so的库来做编译,他可以把.java的程序直接编译成.o文件和可执行文件。并且用gdb调试。
 
本文主要讲述如果使用GDB调试Java程序。关于GDB的使用,请参看我的另一篇文章《用GDB调试程序》。
 
 
用GCJ编译Java程序
 
用GCJ编译Java程序很简单,关于编译成.o和执成文件,如下所示:

gcj -c -g -O MyJavaProg.java
gcj -g --main=MyJavaProg -o MyJavaProg MyJavaProg.o

很明显,基本上就是gcc的语法。当然,你也可以一步编译出可执行文件:
 
            gcj -g --main=MyJavaProg -o MyJavaProg MyJavaProg.java
 
其中,使用-g参数表示加入调试信息,这对于调试时相当重要。不然,无法看到实际的源码和函数。而关于--main参数,意思是指定main函数所在的Java类。
 
如果你需要使用makefile,想使用类似于CFLAGS这样的变量,我们可以使用GCJFLAGS这个变量名。
 
使用GDB调试Java程序
 
如同我的《用GDB调试程序》一文,我使用如下的Java程序作为演示程序。
 
  1 public class sum{
  2    public static long Sum(int n){
  3        long result=0, i;
  4        for(i=0; i<n; i++){
  5            result += i;
  6        }  
  7        return result;
  8    }  
  9   
 10
 11     public static final void main( String argc[] ) {
 12         int i;
 13         int result=0;
 14         for (i=1; i<=100; i++){
 15             result += i;
 16         }  
 17         System.out.println("result = "+result);
 18         System.out.println("result = "+Sum(1000));
 19     }  
 20 }
 
 
下面是程序编译:(注意-g选项)
 
hchen@ubuntu:~/java$ gcj --main=sum -g -o sum sum.java
 
 
进入GDB环境:
 
hchen@ubuntu:~/java$ gdb ./sum
GNU gdb 6.6-debian
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb)
 
你可能在直接使用函数名会有以下问题:
 
(gdb) break sum.main()
Function "sum.main()" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n
 
目前我不知道是否是GDB的bug,不过Workaround的解决方案如下:
1)先List类的构造函数,这样可以找到源文件。
2)使用源文件的行号进行break。
 
(gdb) l sum::sum()
1
2       public class sum{
3          public static long Sum(int n){
4              long result=0, i;
5              for(i=0; i<n; i++){
6                  result += i;
7              }
8              return result;
9          }
10
(gdb) l
11
12          public static final void main( String argc[] ) {
13              int i;
14              int result=0;
15              for (i=1; i<=100; i++){
16                  result += i;
17              }
18              System.out.println("result = "+result);
19              System.out.println("result = "+Sum(1000));
20          }
 
(gdb) break 13
Breakpoint 1 at 0x8048d38: file sum.java, line 13.
 
(gdb) break 16 if i==50
Breakpoint 2 at 0x8048d61: file sum.java, line 16.
 
 
 
 
运行并调式程序:
对于下面出现在GDB命令我不在作过多解释,请参看我的《用GDB调试程序》
 
(gdb) r
Starting program: /home/hchen/java/sum
[Thread debugging using libthread_db enabled]
[New Thread -1243736400 (LWP 18131)]
[New Thread -1245406320 (LWP 18134)]
[Switching to Thread -1243736400 (LWP 18131)]
 
Breakpoint 1, sum.main(java.lang.String[])void (argc=@2bfa8) at sum.java:14
14              int result=0;
Current language:  auto; currently java
 
 (gdb) break sum.Sum               <-----  设置函数断点
Breakpoint 3 at 0x8048b68: file sum.java, line 4.
 
 
(gdb) c
Continuing.
 
Breakpoint 2, sum.main(java.lang.String[])void (argc=@2bfa8) at sum.java:16  <---条件断点  
16                  result += i;
 
(gdb) p result
$2 = 1225
 
(gdb) n
15              for (i=1; i<=100; i++){
 
 
(gdb) c
Continuing.
result = 5050
 
Breakpoint 3, sum.Sum(int)long (n=1000) at sum.java:4                 <-----  函数断点
4              long result=0, i;
(gdb) bt                       <-----  打出函数栈
#0  sum.Sum(int)long (n=1000) at sum.java:4
#1  0x08048edf in sum.main(java.lang.String[])void (argc=@2bfa8) at sum.java:19
#2  0xb6b17611 in gnu::java::lang::MainThread::call_main () from /usr/lib/libgcj.so.81
#3  0xb6b86797 in gnu::java::lang::MainThread::run () from /usr/lib/libgcj.so.81
#4  0xb6b29cf3 in _Jv_ThreadRun () from /usr/lib/libgcj.so.81
#5  0xb6ad77dd in _Jv_RunMain () from /usr/lib/libgcj.so.81
#6  0xb6ad7994 in _Jv_RunMain () from /usr/lib/libgcj.so.81
#7  0xb6ad7a1b in JvRunMain () from /usr/lib/libgcj.so.81
#8  0x08048b38 in main (argc=Cannot access memory at address 0x0) at /tmp/ccKMKFB0.i:11

(gdb) n
5              for(i=0; i<n; i++){
(gdb) n
6                  result += i;
(gdb) n
5              for(i=0; i<n; i++){
(gdb) finish                   <----- 退出函数
Run till exit from #0  sum.Sum(int)long (n=1000) at sum.java:5
0x08048edf in sum.main(java.lang.String[])void (argc=@2bfa8) at sum.java:19
19              System.out.println("result = "+Sum(1000));
Value returned is $1 = 499500
(gdb) n
result = 499500
0xb6b17611 in gnu::java::lang::MainThread::call_main () from /usr/lib/libgcj.so.81
 
 
(gdb) info thread                   <-----  查看线程
  2 Thread -1245553776 (LWP 18143)  0xffffe410 in __kernel_vsyscall ()
* 1 Thread -1243883856 (LWP 18142)  0xb6b17611 in gnu::java::lang::MainThread::call_main () from /usr/lib/libgcj.so.81
(gdb)
 
 
 
 
其它注意事项
 
当你使用GDB调试被GCJ编译的程序时,你需要让GDB忽略SIGPWR和SIGCPU这两个信号。这两个信号被垃圾回收器使用,为了让调试工作进行的更顺利,我们需要使用GDB的命令来忽略这两个信号:
 
(gdb) handle SIGPWR nostop noprint
Signal        Stop      Print   Pass to program Description
SIGPWR        No        No      Yes             Power fail/restart
(gdb) handle SIGXCPU nostop noprint
Signal        Stop      Print   Pass to program Description
SIGXCPU       No        No      Yes             CPU time limit exceeded
 
当然,你并不用每次都需要设置这两个命令,你可以设置$HOME目录下的.gdbinit文件来把这两个命令作为GDB的初始化选项。
 
参考文章
“Compiling Java with GCJ”by Per Bothner
“Java Debugging with gdb”by Matt Welsh
分享到:
评论

相关推荐

    gdb 调试gdb 调试gdb 调试gdb 调试gdb 调试

    gdb 调试gdb 调试gdb 调试gdb 调试gdb 调试

    STM32 GDB调试手册

    STM32 GDB 调试手册 本手册详细介绍了如何使用 GDB 调试 STM32 微控制器,旨在帮助开发者快速掌握 GDB 调试工具的使用方法,以便更好地 debug STM32 项目。 标题解释 STM32 GDB 调试手册是 STM 官方提供的调试指南...

    GDB调试手册(中文版)

    GDB调试手册是一份非常适合初学者的中文资源,它详尽地介绍了如何使用GDB这一强大的开源调试工具。GDB,全称GNU调试器,是Linux和其他类Unix系统下用于调试C、C++、Objective-C、Fortran等语言编写的程序的工具。...

    GDB调试精粹及使用实例GDB调试精粹及使用实例

    GDB调试精粹及使用实例GDB调试精粹及使用实例GDB调试精粹及使用实例GDB调试精粹及使用实例GDB调试精粹及使用实例GDB调试精粹及使用实例GDB调试精粹及使用实例GDB调试精粹及使用实例GDB调试精粹及使用实例GDB调试精粹...

    GDB调试程序[陈皓]&GDB调试工具指南

    《GDB调试程序[陈皓]》与《GDB调试工具指南》这两份PDF文档,是深入了解和精通GDB(GNU Debugger)这一强大调试工具的重要参考资料。GDB是开源世界中广泛使用的C、C++以及其他多种语言的调试器,它允许开发者在运行...

    用GDB调试程序(整理有书签).pdf

    在IT行业中,调试是软件开发过程中的重要环节,特别是在Linux环境下,GDB(GNU Debugger)作为一款强大的命令行调试工具,被广泛应用于C/C++等语言的程序调试。本篇文章将详细阐述GDB的使用方法及其核心功能,帮助你...

    gdb调试 rk可运行程序

    GDB(GNU Debugger)是一款强大的源代码级调试器,广泛应用于Linux和其他Unix-like操作系统中,用于调试C、C++以及其他支持的编程语言编译出的程序。在这个场景中,我们将聚焦于如何使用GDB来调试基于ARM AArch64...

    用GDB调试程序 用GDB调试程序

    GDB 调试程序 GDB 是一个强大的命令行调试工具,主要帮忙用户完成四个方面的功能:启动程序、设置断点、检查程序状态和动态改变程序执行环境。GDB 的强大之处在于其可以形成执行序列,形成脚本,非常容易集成在一起...

    Linux课件:GDB调试简明指南.pdf

    Linux 调试工具 GDB 简明指南 GDB(GNU Debugger)是 Linux 系统中的一款强大调试工具,用于调试 C 和 C++ 程序。它使用户可以在程序运行时观察程序的内部结构和内存的使用情况。GDB 提供了多种功能,如监视程序中...

    GDB调试应用指南Guide.pdf

    GDB能够调试多种编程语言编写的程序,包括但不限于Ada、C、C++、汇编、D、Fortran、Objective-C、Go、Java、Pascal等。在介绍GDB调试程序的方法前,了解如何判断程序是否包含调试信息是第一步。 通常情况下,想要...

    gdb调试详解 讲述如何使用gdb调试以及调试core dump

    gdb调试详解 GDB(GNU Debugger)是一款功能强大的调试工具,广泛应用于Linux平台。通过GDB,可以轻松地调试程序,检查程序崩溃后的核心转储文件(Core Dump),从而帮助开发者快速定位和解决问题。 GDB基础命令 ...

    GDB调试--以汇编语言为例

    "GDB调试--以汇编语言为例" 本文将详细介绍如何使用GDB调试汇编语言程序,包括使用CPUID指令来获取CPU信息,并使用GDB调试器来调试汇编语言程序。 一、CPUID指令 CPUID指令是Intel Pentium级别以上CPU中的一个...

    GDB--gdb调试.pdf

    GDB支持多种编程语言,如C、C++、Ada、Objective-C、Pascal、Go、Java等,使其成为跨语言调试的理想选择。 ### 使用GDB进行调试 #### 启动与退出GDB - **启动GDB**:通过命令行调用`gdb [options] [target] [args...

    Linux下gdb调试工具指南

    ### Linux下gdb调试工具指南:深入解析与实践 #### 引言 在软件开发领域,尤其是对于系统级编程和复杂应用的维护,一个高效、功能全面的调试工具是不可或缺的。GNU调试器(GDB)正是这样一款强大的调试工具,它...

    gdb调试多线程程序总结

    GDB 调试多线程程序总结 GDB 调试多线程程序是一种复杂的调试技术,需要熟练掌握 GDB 的多线程调试命令。下面是 GDB 调试多线程程序的总结。 基本命令 在 GDB 中,使用 `info threads` 命令可以显示当前可调试的...

    GDB 调试 中文手册

    GDB 调试中文手册 GDB(GNU Debugger)是一款功能强大且广泛使用的 UNIX 下的程序调试工具。它提供了多种功能,包括启动程序、设置断点、观察点和捕捉点、维护停止点、停止条件维护、为停止点设定运行命令、恢复...

Global site tag (gtag.js) - Google Analytics