使用jdb和gdb同时调试java native code

主要流程:

1.使用jdb启动生成的java字节码或jar包;

2.java源码中适当的位置设置断点,如main方法;

3.使用run指令,使代码跑起来。(jdb命令只是进入调试环境,jvm还没有运行起来,当然我们的代码也没有运行起来,run指令之后,才有创建一个jvm,在进程列表中会发现多出一个java进程。)

4.另起一个终端,使用查询新生成的java进程的进程id

5.使用gdb命令附着到上述java进程。

6.C源码中适当的位置设置断点。

至此,两个调试都挂在了同一个jvm上。需要注意,只有两个调试器同时允许程序执行是,程序才会继续运行。


HelloWorld为例说明:

第一至第三步如下:

$jdb HelloWorld	 <= 第一步
正在初始化jdb...
>stop in HelloWorld.main	<= 第二步,设置适当的断点
正在延迟断点HelloWorld.main。
将在加载类后设置。
>run	 <= 第三步, run起来
运行HelloWorld
设置未捕获的java.lang.Throwable
设置延迟的未捕获的java.lang.Throwable
>
VM已启动:设置延迟的断点HelloWorld.main
断点命中:"线程=main",HelloWorld.main(), 行=4bci=0
4 HelloWorld hw = new HelloWorld();
main[1]
然后另起一个终端,继续第四步至第六步

$ps -e | grep java
32160pts/24 00:00:00 java
$sudo gdb -p 32160
GNUgdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
Copyright(C) 2014 Free Software Foundation, Inc.
//××××此处有打断输出,省略
Loadedsymbols for /usr/lib/jvm/jdk1.7.0_07/jre/lib/i386/libdt_socket.so
Readingsymbols from/home/luyao/java/jni/HelloWorld/libHelloWorld.so...done.
Loadedsymbols for /home/luyao/java/jni/HelloWorld/libHelloWorld.so <= 可以看到已经加载我们的库了
0xb775f428in __kernel_vsyscall ()
(gdb)break HelloWorld.c:Java_HelloWorld_print <=设置一个断点,在native的print函数前
Breakpoint1 at 0xb682d58d: file HelloWorld.c, line 8.
(gdb)c	<= gdb释放阻塞。还需jdb调用cont命令,程序才会继续执行。
Continuing.

回到第一个终端,执行cont命令

第一个终端里:

main[1]cont
InJava, before> native
第二个终端里:

(gdb)c
Continuing.
[Switchingto Thread 0xb68edb40 (LWP 32164)]
Breakpoint1, Java_HelloWorld_print (jenv=0xb6707d28, jobj=0xb68ed010)
atHelloWorld.c:8
8	 inti = 0;	 <= Nice! 程序已经断在gdb里了。
(gdb)//然后就可以任意发挥了。

几个问题:

1)在上述C代码中,有一句注释掉的代码fflush(stdout);如果没有该行代码,在jdb调试器中,输出会发生乱序(native函数中的输出被延迟甚至漏掉)。单独挂gdb调试器时,没有这种问题。估计跟JVM中实现native层标准输出设备缓存的方法有关。等进一步研究JVM的实习后再来关注这个问题。

2)由于java对线程id描述的方式不同,目前无法确定从java代码调用native代码时是否发生线程切换,很大程度上感觉不会发生线程切换。在《Insidethe Java VirtualMachine》一书中只讲到从java调用native时只发生了线程栈的切换,即native代码有独立的栈,(这类似与用户态栈和内核态栈的区别)。没有线程的切换。但是这本书的编写时间较早,2000年,当时java应该还是1.3或者1.4,不知道在1.7,或者说在安卓的Dalvik虚拟机中的实现是否有不同,需进一步研究。

3)一个很困惑的问题。在启动gdb时,使用的时root权限,如果不用root权限,会报错,如下:

Attachingto process 7093
Couldnot attach to process. If your uid matches the uid of the target
process,check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
againas the root user. For more details, see/etc/sysctl.d/10-ptrace.conf
ptrace:不允许的操作.

不明白的是,非 root 下,我的 uid 肯定是一致的,即目标进程 java uid 也是当前用户。而使用 root 权限启动 gdb 时, gdb uid 显然与目标进程 java uid 不一致了

$ps -aux | grep java
luyao 7093 0.0 0.6 688660 13812 pts/24 tl+ 00:14 0:00/usr/lib/jvm/jdk1.7.0_07/jre/bin/java -Xdebug-Xrunjdwp:transport=dt_socket,address=luyao-K40IN:55455,suspend=yHelloWorld
luyao 8765 0.0 0.0 6116 824 pts/7 S+ 00:20 0:00 grep--color=auto java
luyao@luyao-K40IN:~/java/jni/HelloWorld$ps -aux | grep gdb
root 8611 0.0 0.0 8312 2036 pts/12 S+ 00:19 0:00 sudo gdb-p 7093
root 8612 0.8 1.4 40216 30012 pts/12 S+ 00:19 0:00 gdb -p7093
luyao 8857 0.0 0.0 6116 824 pts/7 S+ 00:20 0:00 grep--color=auto gdb
查看上述错误提示中提到的文档如下:

$cat /etc/sysctl.d/10-ptrace.conf
#The PTRACE system is used for debugging. With it, a single userprocess
#can attach to any other dumpable process owned by the same user. Inthe
#case of malicious software, it is possible to use PTRACE to access
#credentials that exist in memory (re-using existing SSH connections,
#extracting GPG agent information, etc).
#
#A PTRACE scope of "0" is the more permissive mode. A scopeof "1" limits
#PTRACE only to direct child processes (e.g. "gdbname-of-program" and
#"strace -f name-of-program" work, but gdb's "attach"and "strace -fp $PID"
#do not). The PTRACE scope is ignored when a user has CAP_SYS_PTRACE,so
#"sudo strace -fp $PID" will work as before. For moredetails see:
#https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace
#
#For applications launching crash handlers that need PTRACE,exceptions can
#be registered by the debugee by declaring in the segfault handler
#specifically which process will be using PTRACE on the debugee:
# prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);
#
#In general, PTRACE is not needed for the average running Ubuntusystem.
#To that end, the default is to set the PTRACE scope to "1". This value
#may not be appropriate for developers or servers with only adminaccounts.
kernel.yama.ptrace_scope= 1
$cat /proc/sys/kernel//yama/ptrace_scope
1

好吧,应该是ubuntu自身的安全机制导致, /proc/sys/kernel//yama/ptrace_scope 中的值改为 0 就可以了。





  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要进行Java程序的调试和测试,可以使用以下工具和方法: 1. 使用集成开发环境(IDE):常用的Java开发环境(如Eclipse、IntelliJ IDEA和NetBeans)都提供了调试功能。你可以在IDE中设置断点,逐行执行程序,并观察变量的值和程序的执行流程。 2. 打印日志:在关键的代码段中插入打印语句,输出变量的值和程序执行的状态。通过查看日志信息,可以了解程序的执行过程和可能存在的问题。 3. 使用调试工具:Java提供了一些调试工具,例如jdbJava Debugger)和jconsole。jdbJava的命令行调试器,可以通过命令行界面进行程序的调试。jconsole是Java监视和管理控制台,可以用于监视Java应用程序的性能和调试信息。 4. 单元测试框架:使用单元测试框架(如JUnit或TestNG)编写测试用例,并对程序中的不同模块进行单元测试。通过运行测试用例,可以验证程序的正确性,并及早发现潜在的问题。 5. 调试工具和技术:除了IDE和命令行调试器外,还有一些第三方调试工具和技术可供选择。例如,VisualVM可以监视Java应用程序的性能,并提供线程分析和堆转储等功能。另外,还有一些开源的调试工具,如JProfiler和YourKit,提供更丰富的调试和性能分析功能。 总之,调试和测试是开发过程中不可或缺的环节。通过合理使用工具和方法,可以提高程序的质量和稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值