TL;DR:问题是GDB缺乏对内部musl信号的支持
gdb ticket
.
这里提供了一个快速且脏的修补GDB:
https://github.com/shaharv/alpine-gdb-builds/releases/tag/v0.1
补丁提交:
shaharv/binutils-gdb@0ca9c66
.
然后,可以使用
handle SIGSYNCCALL nostop noprint pass
.
调试Alpine OpenJDK java时发生的gdb崩溃可以通过以下方式进行处理:
-
-
break os::init_2
-
使用所需的命令行参数运行java
-
set MaxFDLimit=0
-
继续,并正常调试。
我已经用openjdk8和11early-access测试了解决方法,所以它很可能也可以用openjdk9和10。
不幸的是,此解决方案的范围非常有限:
-
它只在JDK有调试符号的情况下工作-无论是本地调试OpenJDK构建还是使用
openjdk8-dbg
-
它只适用于命令行gdb,不适用于CLion和eclipsecdt等gdb前端。
总结:
碰撞发生在
setrlimit
函数在gdb内部调用。穆斯
实现向线程发送信号
SIGSYNCCALL
,它不受gdb支持,结果为
Unknown signal
错误。为了避免错误,相关的初始化代码
JavaMain
通过关闭
MaxFDLimit
全局变量。
完整解释:
在JVM初始化过程中
创建本机线程,并创建VM。在虚拟机创建过程中,有一个特定于操作系统的初始化
设置限制
被称为。下面是堆栈跟踪的相关部分:
#0 __synccall (func=func@entry=0x7ffff7da2662 <do_setrlimit>, ctx=ctx@entry=0x7ffff7ff4720) at src/thread/synccall.c:48
#1 0x00007ffff7da26a1 in setrlimit (resource=resource@entry=7, rlim=rlim@entry=0x7ffff7ff4750) at src/misc/setrlimit.c:42
#2 0x00007ffff73bd1fe in os::init_2 ()
at /home/buildozer/aports/community/openjdk8/src/icedtea-3.8.0/openjdk/hotspot/src/os/linux/vm/os_linux.cpp:5096
#3 0x00007ffff746177d in Threads::create_vm (args=0x7ffff7ff4a20, canTryAgain=canTryAgain@entry=0x7ffff7ff4987)
at /home/buildozer/aports/community/openjdk8/src/icedtea-3.8.0/openjdk/hotspot/src/share/vm/runtime/thread.cpp:3361
#4 0x00007ffff729cd48 in JNI_CreateJavaVM (vm=0x7ffff7ff4a10, penv=0x7ffff7ff4a18, args=<optimized out>)
at /home/buildozer/aports/community/openjdk8/src/icedtea-3.8.0/openjdk/hotspot/src/share/vm/prims/jni.cpp:5221
#5 0x00007ffff7b61b0b in InitializeJVM (ifn=<synthetic pointer>, penv=0x7ffff7ff4a18, pvm=0x7ffff7ff4a10)
at /home/buildozer/aports/community/openjdk8/src/icedtea-3.8.0/openjdk/jdk/src/share/bin/java.c:1231
#6 JavaMain (_args=<optimized out>)
函数调用。
穆斯
设置限制
实施是
AS-Safe
,这意味着从异步信号处理程序调用它是安全的。正在通过调用
__synccall
(
setrlimit.c
int setrlimit(int resource, const struct rlimit *rlim)
{
struct ctx c = { .res = resource, .rlim = rlim, .err = -1 };
__synccall(do_setrlimit, &c);
if (c.err) {
if (c.err>0) errno = c.err;
return -1;
}
return 0;
}
__同步呼叫
synccall.c
)阻塞所有信号,然后迭代进程的所有线程并向它们发送
SIGSYNCCALL公司
do_setrlimit
已执行):
r = -__syscall(SYS_tgkill, pid, tid, SIGSYNCCALL);
SIGSYNCCALL公司
SIGSYNCCALL公司
不包括在处理的信号中(见gdb
signals.c
). 因此,当信号被提升时,gdb终止于
未知信号
解决方法:
设置限制
在OpenJDK中运行。相关代码在
os::init_2
os_linux.cpp
):
if (MaxFDLimit) {
// set the number of file descriptors to max. print out error
// if getrlimit/setrlimit fails but continue regardless.
struct rlimit nbr_files;
int status = getrlimit(RLIMIT_NOFILE, &nbr_files);
if (status != 0) {
if (PrintMiscellaneous && (Verbose || WizardMode))
perror("os::init_2 getrlimit failed");
} else {
nbr_files.rlim_cur = nbr_files.rlim_max;
status = setrlimit(RLIMIT_NOFILE, &nbr_files);
if (status != 0) {
if (PrintMiscellaneous && (Verbose || WizardMode))
perror("os::init_2 setrlimit failed");
}
}
}
通过设置
最大FDLIMIT
如果设置为0,则不会执行上述代码,并且VM初始化可以正常继续。有一个命令行选项可以切换这个变量,
-XX:-MaxFDLimit
Solaris only
,所以我们别无选择,只能在gdb中手动关闭这个变量。
最大FDLIMIT
是历史的,用于在具有非常低的默认FD限制(256)的古代系统上增加文件描述符的默认限制,如中所述
JDK-8010126
ulimit
,而不是JVM本身。