Skip to content

Debug with GDB in QEMU user mode

dingli edited this page Apr 26, 2021 · 4 revisions

使用GDB进行远程调试

当我们在x86主机上使用QEMU用户模式开发运行RV32G JDK的时候,需要用到GDB进行远程调试。

假设我们构建的镜像路径为/path/to/jvm/openjdk-11.0.9-internal,其实也就是JDK的路径。以下没有使用脚本包裹JDK二进制文件,而是显式的使用qemu命令来进行的。

首先进入到构建好的JDK镜像的bin目录下,然后在后台使用QEMU执行java -version:

$ cd /path/to/jvm/openjdk-11.0.9-internal/bin
$ /path/to/qemu/bin/qemu-riscv32 -L /path/to/riscv32/sysroot -g 33334 ./java -version &

接着运行工具链中的GDB:

$ /path/to/riscv32/bin/riscv32-unknown-linux-gnu-gdb --args ./java -version

之后就进入到了GDB调试,显示如下:

GNU gdb (GDB) 10.1
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=riscv32-unknown-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./java...
(gdb)

接下来在GDB中连接调试端口(最开始步骤中-g后的参数即为端口号):

(gdb) target remote localhost:33334

我们通常将main作为第一个断点,这样就会在src/java.base/share/native/launcher/main.c中的JLI_InitArgProcessing(jargc > 0, const_disable_argfile);这里打上断点:

(gdb) b main

这里直接执行c的话会提示:

(gdb) c
Continuing.
warning: Could not load shared library symbols for /home/dingli/isrc-jdk11u/jdk11u/fastdebug_32_core/jvm/openjdk-11.0.9-internal/bin/../lib/jli/libjli.so.
Do you need "set solib-search-path" or "set sysroot"?

所以我们需要添加shared library的path,多个路径用:隔开:

(gdb) set solib-search-path /path/to/jvm/openjdk-11.0.9-internal/lib:/path/to/jvm/openjdk-11.0.9-internal/lib/jli:/path/to/jvm/openjdk-11.0.9-internal/lib/server

之后我们需要确认一下我们刚刚的配置是否生效:

(gdb) i shared

输出结果中的Syms一列都为yes即设置成功。

接着我们在需要调试的地方打上断点(以macroAssembler_riscv32.cpp:2632为例):

(gdb) b macroAssembler_riscv32.cpp:2632
No source file named macroAssembler_riscv32.cpp.
Make breakpoint pending on future shared library load? (y or [n])
(gdb) y

继续输入c往下调试:

(gdb) c
Continuing.
[New Thread 1.24834]

Thread 1 received signal SIGTRAP, Trace/breakpoint trap.
__pthread_clockjoin_ex (threadid=1057465360, thread_return=thread_return@entry=0x407fbf38, clockid=clockid@entry=0, abstime=abstime@entry=0x0, 
    block=block@entry=true) at pthread_join_common.c:148
148                 lll_futex_wait_cancel (&pd->tid, tid, LLL_SHARED);

这里会出现上述的报错,我们需要切换到第二个进程,查看当前的进程:

(gdb) i th
  Id   Target Id                        Frame 
* 1    Thread 1.20217 (CPU#0 [running]) __pthread_clockjoin_ex (threadid=1057465360, thread_return=thread_return@entry=0x407fbf38, clockid=clockid@entry=0, 
    abstime=abstime@entry=0x0, block=block@entry=true) at pthread_join_common.c:148
  2    Thread 1.24834 (CPU#1 [running]) __GI__dl_debug_state () at dl-debug.c:74

切换到第二个进程:

(gdb) t 2
[Switching to thread 2 (Thread 1.24834)]
#0  __GI__dl_debug_state () at dl-debug.c:74
74      }
(gdb) c
Continuing.

之后便可以正常调试了。