Docker 容器中 Java 调用 C++ 动态库问题诊断

本想用一篇日志记录下在 Docker 容器中使用 Java 调用 C++ 动态库时,当 C++ Crash 时如何自动生成 core dump, 不想分成了至少三篇来完成这一研究。 可以回顾一下前两篇日志

  1. C++ 调用 C++ 动态库时问题诊断
  2. Java 使用 JNA 调用 C++ 动态库问题诊断

本文是基于第二篇进一步推进,继续探索如何在 Docker 容器中 Java 调用 C++ 动态库时的 core dump 如何生成。首先测试的平台依然是 AWS EC2 实例, OS 为 Amazon Linux 2023, Docker 版本为 25.0.14。为叙事方便,本文所用代码与上篇一样,但还是重复一遍,省却了连接跳转。

准备能触发 Crash 的动态库

C++ 动态还是用上篇一样的例子,当输入为 devil 时产生重复释放内存从而导致 Crash。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <string.h>

extern "C" {
    void sayHello(char* name) {
        printf("hello %s\n", name);

        if (strcmp(name, "devil") == 0) {
            char* buf = new char[64];
            strcpy(buf, "you are devil!");

            printf("buf: %s\n", buf);

            delete[] buf;
            delete[] buf;
        }
    }
}

编译生成动态库 libhello.so,带上 -g 参数让代码行信息保留在二进制文件中,命令如下

1g++ -g -shared -fPIC -o libhello.so hello.cpp

创建使用 C++ 动态库的 Java 代码

我们将采用 JNA, 只依赖于单一文件,当前版本为 5.18.1, 可从 5.18.1 下载. JDK 版本将使用版本 21, 应用 Java 18 的 Simple Web Server API 来搭建一个简单的 Web Server。

Test.java

 1import com.sun.jna.*;
 2import com.sun.net.httpserver.*;
 3
 4import java.net.InetSocketAddress;
 5
 6public class Test {
 7    interface HelloLibrary extends Library {
 8        void sayHello(String name);
 9    }
10
11    public static void main(String[] args) throws Exception {
12        var library = Native.load("./libhello.so", HelloLibrary.class);
13        System.out.println("loaded shared library");
14
15        HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
16
17        server.createContext("/ok", exchange -> {
18            library.sayHello("world");
19            exchange.sendResponseHeaders(200, 0);
20            exchange.close();
21        });
22        server.createContext("/fail", exchange -> {
23            library.sayHello("devil");
24            exchange.sendResponseHeaders(500, 0);
25            exchange.close();
26        });
27
28        server.start();
29        System.out.println("Server started on port 8080");
30    }
31}

构建 Docker 镜像

先创建一个 Dockerfile, 其内容为

1FROM amazoncorretto:21
2
3COPY jna-5.18.1.jar ./
4COPY Test.java ./
5COPY libhello.so ./
6
7ENTRYPOINT java $JAVA_OPTS -cp jna-5.18.1.jar Test.java

创建 Docker 镜像

1docker build -t java-web .

测试 Docker 容器

在不确定 Docker 容器中运行的 Java 进程(其实就是宿主机上的一个进程) 产生 core dump 与否是不是与宿主机的 ulimit 等设置有关,但在宿主机上执行

1ulimit -c unlimited
2sysctl -w kernel.core_pattern=/data/crash_logs/core.%p

然后启动容器中的 Web 服务,映射端口号

1docker run -it -p 8080:8080 java-web:latest
2loaded shared library
3Server started on port 8080

在另一个终端中访问 /ok/fail 两个接口

1[root@lin-0aff3de6 ~]# curl http://localhost:8080/ok
2[root@lin-0aff3de6 ~]#
3[root@lin-0aff3de6 ~]# curl http://localhost:8080/fail
4[root@lin-0aff3de6 ~]# curl http://localhost:8080/fail
5[root@lin-0aff3de6 ~]# curl http://localhost:8080/fail
6curl: (52) Empty reply from server

测试 http://localhost:8080/fail 后服务器还不会立即死的,进行了几 次 /fail 请求后最终看到服务器支撑不住了, 其实第一个 /fail 调用就触发要崩溃,只是产生了延迟。服务端的输出是

 1hello world
 2hello devil
 3buf: you are devil!
 4hello devil
 5buf: you are devil!
 6double free or corruption (fasttop)
 7#
 8# A fatal error has been detected by the Java Runtime Environment:
 9#
10#  SIGSEGV (0xb) at pc=0x00007f29394a323b, pid=1, tid=32
11#
12# JRE version: OpenJDK Runtime Environment Corretto-21.0.10.7.1 (21.0.10+7) (build 21.0.10+7-LTS)
13# Java VM: OpenJDK 64-Bit Server VM Corretto-21.0.10.7.1 (21.0.10+7-LTS, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
14# Problematic frame:
15# C  [libc.so.6+0x3523b]  abort+0x23b
16#
17# Core dump will be written. Default location: /data/crash_logs/core.1
18#
19# An error report file with more information is saved as:
20# //hs_err_pid1.log
21#
22# If you would like to submit a bug report, please visit:
23#   https://github.com/corretto/corretto-21/issues/
24# The crash happened outside the Java Virtual Machine in native code.
25# See problematic frame for where to report the bug.
26#

后面几行给出了两个信息,可以生成 core dump 文件,在 /data/crash_logs/core.1, 还生成了 hs_err.log 文件,在 //hs_err_pid1.log, 因为指定的 /data/crash_logs/core.%p, 所以无论生成的 core dump 是在容器内还是宿主机上,都是在 /data/crash_logs 目录下,可是没有。

docker ps -a 找到已死的容器 ID, docker start <container-id> 复活它,再 docker exec -it <container-id> bash 进到容器中,可以看到 /hs_err_pid1.log 文件。 从该 /hs_err_pid1.log 可看到引起 crash 的原因

1j  com.sun.jna.Library$Handler.invoke(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;+390
2j  $Proxy0.sayHello(Ljava/lang/String;)V+16
3j  Test.lambda$main$1(LTest$HelloLibrary;Lcom/sun/net/httpserver/HttpExchange;)V+3
4j  Test$$Lambda+0x00007f28bc181a28.handle(Lcom/sun/net/httpserver/HttpExchange;)V+5

如果我们起动容器时加上 -e JAVA_TOOL_OPTIONS="-XX:ErrorFile=/data/crash_logs/hs_err_%p.log" 将来能看在宿主机的 /data/crash_logs 目录下生成 hs_err_1.log 文件.

docker exec -it <container-id> bash 进到容器,观察两个值

1bash-4.2# ulimit -c
2unlimited
3bash-4.2# cat /proc/sys/kernel/core_pattern
4/data/crash_logs/core.%p

从上面看到的 /data/crash_logs/java-core.1 说明真的是宿主机上的 kernel.core_pattern=java-core.%p 影响着容器中运行的 Java 进程。 还是那句话, 说是容器的进程,本就是宿主机上的的进程,因为加了进程 namespace. 比如运行了 docker run -it -p 8080:8080 java-web:latest, 我们用 docker exec -it <container-id> sh 进到容器后看到的 Java 进程是

1sh-4.2# ps -ef|grep Test.java
2root           1       0  2 14:40 pts/0    00:00:01 java -cp jna-5.18.1.jar Test.java

而在宿主机上, 看到的 Java 进程是

1[root@lin-0aff3de6 ~]# ps -ef|grep Test.java
2root      124386  124360  0 14:40 ?        00:00:02 java -cp jna-5.18.1.jar Test.java

容器内 PID:1 与 容器外 PID: 124386 实际是同一个进程,只是生成 core dump 时用了容器内的 PID:1. 假如启动 Docker 容器时用了 --pid=host 参数,那么容器里外看到的该 Java 进程 PID 值就完全一样了。

测试宿主机 ulimit -c 对容器的影响

试图用 ulimit -c 0

1[root@lin-0aff3de6 ~]# ulimit -c 0
2[root@lin-0aff3de6 ~]# ulimit -c
30
4[root@lin-0aff3de6 ~]# docker run -it -p 8080:8080 java-web:latest

观察容器内部

1[root@lin-0aff3de6 ~]# docker exec 7f2 bash -c "ulimit -c && cat /proc/sys/kernel/core_pattern"
2unlimited
3java-core.%p

有两种情况,要么 docker 命令启动容器时不受 ulimit -c 0 会话的影响,或者 docker 启动时默认设置了 ulimit -c unlimited. 又试了修改 /etc/security/limits.conf 中的 soft, hard 都为 0 后在容器上的 ulimit -c 都是 unlimited.

因为 Docker 容器设置 ulimit -c unlimited 是默认行为,除非启动 Docker 容器时用了 docker run --ulimit core=0 参数重置 ulimit -c.

阶段小结

学到这里,我们有必要来个小节, 两个关键知识点

  1. Docker 默认 ulimit -c unlimited, 除非用 --ulimit core=0 明确指定值
  2. 容器与宿主机共享相同的 /proc/sys/kernel/core_pattern 配置,可以在启动容器时用命令 sysctl -w kernel.core_pattern=java-core.%p 配置,或修改 /etc/sysctl.conf 使之永久有效

加了 -XX:+CreateCoredumpOnCrash 也没有 coredump 文件生成

1sysctl -w kernel.core_pattern=/data/crash_logs/java-core.%p
2docker run -it -v /data/crash_logs:/data/crash_logs \
3  -e JAVA_TOOL_OPTIONS="-XX:ErrorFile=/crash_logs/hs_err_%p.log -XX:+CreateCoredumpOnCrash" \
4  -p 8080:8080 java-web:latest

后来还试过启动 Docker 容器的加更多的参数,如

1sysctl -w kernel.core_pattern=/data/crash_logs/java-core.%p
2docker run -v /data/crash_logs:/data/crash_logs \
3   --ulimit core=-1 --privileged --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
4  -e JAVA_TOOL_OPTIONS="-XX:ErrorFile=/data/crash_logs/hs_err_%p.log -XX:+CreateCoredumpOnCrash" \
5  -p 8080:8080 java-web:latest

通通都不管用, 怎么都不生成 core dump 文件, 即使 Docker 基础镜像由 amazoncorretto:21 改成 amazoncorretto:21-2023 也无济于事。

尝试非 Web Java 应用

修改之前的 Java 代码,改为

 1import com.sun.jna.*;
 2
 3public class Test {
 4    interface HelloLibrary extends Library {
 5        void sayHello(String name);
 6    }
 7
 8    public static void main(String[] args) throws Exception {
 9        var library = Native.load("./libhello.so", HelloLibrary.class);
10        System.out.println("loaded shared library");
11        library.sayHello(args[0]);
12    }
13}

Dockerfile 也相应修改

1FROM amazoncorretto:21
2
3COPY jna-5.18.1.jar ./
4COPY Test.java ./
5COPY libhello.so ./
6
7
8ENTRYPOINT ["java", "-cp", "jna-5.18.1.jar", "Test.java"]

因为要通过命令行参数来触发 Crash. 生新构建 docker build -t java-console . 镜像后,

执行

 1docker run java-console world
 2loaded shared library
 3hello world
 4
 5sysctl -w kernel.core_pattern=/data/crash_logs/java-core.%p
 6docker run -v /data/crash_logs:/data/crash_logs   \
 7  --ulimit core=-1 --privileged --cap-add=SYS_PTRACE --security-opt seccomp=unconfined  \
 8  -e JAVA_TOOL_OPTIONS="-XX:ErrorFile=/data/crash_logs/hs_err_%p.log -XX:+CreateCoredumpOnCrash" \
 9  java-dll-core-dump:latest devil
10Picked up JAVA_TOOL_OPTIONS: -XX:ErrorFile=/data/crash_logs/hs_err_%p.log -XX:+CreateCoredumpOnCrash
11loaded shared library
12double free or corruption (fasttop)
13#
14# A fatal error has been detected by the Java Runtime Environment:
15#
16#  SIGSEGV (0xb) at pc=0x00007f116926523b, pid=1, tid=7
17#
18# JRE version: OpenJDK Runtime Environment Corretto-21.0.10.7.1 (21.0.10+7) (build 21.0.10+7-LTS)
19# Java VM: OpenJDK 64-Bit Server VM Corretto-21.0.10.7.1 (21.0.10+7-LTS, mixed mode, sharing, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
20# Problematic frame:
21# C  [libc.so.6+0x3523b]  abort+0x23b
22#
23# Core dump will be written. Default location: /data/crash_logs/java-core.1
24#
25# An error report file with more information is saved as:
26# /data/crash_logs/hs_err_1.log
27#
28# If you would like to submit a bug report, please visit:
29#   https://github.com/corretto/corretto-21/issues/
30#

coredump 没有,还是没有。

换成 C++ 调动动态库的方式

Dockerfile 的内容为

1FROM amazonlinux:2023
2
3COPY hello ./
4COPY libhello.so ./
5ENV LD_LIBRARY_PATH=./
6
7ENTRYPOINT ["./hello"]

运行

1sysctl -w kernel.core_pattern=/data/crash_logs/core.%p
2docker run cpp-chello world
3hello world
4docker run -v /data/crash_logs:/data/crash_logs cpp-hello devil
5free(): double free detected in tcache 2

可是在 /data/crash_logs 目录下生成了 core.1 文件。

gdb ./hello core.1, 再 bt 就能看到调用栈了

1Missing rpms, try: dnf --enablerepo='*debug*' install libstdc++-debuginfo-14.2.1-7.amzn2023.0.2.x86_64 glibc-debuginfo-2.34-231.amzn2023.0.3.x86_64 libgcc-debuginfo-14.2.1-7.amzn2023.0.2.x86_64
2(gdb) bt
3#0  0x00007f6381b24918 in abort () from /lib64/libc.so.6
4#1  0x00007f6381b251b2 in __libc_message.cold () from /lib64/libc.so.6
5#2  0x00007f6381b920d7 in malloc_printerr () from /lib64/libc.so.6
6#3  0x00007f6381b943ab in _int_free () from /lib64/libc.so.6
7#4  0x00007f6381b96923 in free () from /lib64/libc.so.6
8#5  0x00000000004011f9 in sayHello (name=0x7fffc33cdf74 "devil") at hello.cpp:14
9#6  0x0000000000401244 in main (argc=2, argv=0x7fffc33cd3d8) at hello.cpp:24

注意把 hellolibhello.so 都放在与 core.1 同一目录下,否则 gdb 无法解析出调用栈中的函数名和行号信息。

看来是 JNA 在从中作祟。

恢复让 coredumpctl 管理的方式

1# 这是改的内存中的值
2echo '|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h' | sudo tee /proc/sys/kernel/core_pattern
3
4# 如果在 /etc/sysctl.conf 没有相关的配置, 执行 sysctl --system 也能恢复到上面的值
5cat /proc/sys/kernel/core_pattern
6|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h

现在执行 Java Web 的 Docker 镜像, 访问 /fail 接口出错后在服务端显示为

1docker run -p 8080:8080 java-web
2...
3# Core dump will be written. Default location: Core dumps may be processed with "/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h" (or dumping to //core.1)

说明被 systemd-coredump 接管了,但这次测试用 coredumpctl 没有看到任何记录,还有些问题。查看 systemd-coredump 的日志

1[root@lin-0aff3de6 ~]# journalctl -u "systemd-coredump*" --no-pager
2Feb 24 03:04:12 lin-0aff3de6.example.com systemd[1]: systemd-coredump.socket: Deactivated successfully.
3Feb 24 03:04:12 lin-0aff3de6.example.com systemd[1]: Closed systemd-coredump.socket - Process Core Dump Socket.
4-- Boot 5bcfe0586e0643dbbef47fe92eb6483e --
5Feb 25 03:03:33 lin-0aff3de6.example.com systemd[1]: systemd-coredump.socket: Deactivated successfully.
6Feb 25 03:03:33 lin-0aff3de6.example.com systemd[1]: Closed systemd-coredump.socket - Process Core Dump Socket.

大概是 Java 进程退的太快了。

如果用 journalctl -u "systemd-coredump*" --no-pager 看到 Timeout 的信息时,因为默认 5 分钟,但 core dump 太多,超时就会放弃。 那就得修改 RuntimeMaxSec 的值,改成无限制,或某个更大的值,修改方法如下

 1sudo mkdir -p /etc/systemd/system/systemd-coredump@.service.d/
 2sudo tee /etc/systemd/system/systemd-coredump@.service.d/override.conf <<EOF
 3[Service]
 4RuntimeMaxSec=infinity
 5EOF
 6
 7sudo systemctl daemon-reload
 8
 9# 查看修改后的值,如果显示为 infinity 就说明修改成功了,出现多个值的话,最后那个值生效
10systemctl cat systemd-coredump@.service | grep RuntimeMaxSec

-fsanitize=address 仍然有效

在编译 hello.cpp 时加上 -fsanitize=address 参数,生成的动态库在触发 Crash 后能看到更详细的错误信息

1g++ -shared -fPIC -fsanitize=address -g -o libhello.so hello.cpp

Dockerfile 中要安装 libasan 包,通过 yum search libasan 安装相应的版本,或安装 gcc 时会自动安装 libasan 包. 现在的 Dockerfile 如下

1FROM amazoncorretto:21
2
3COPY jna-5.18.1.jar ./
4COPY Test.java ./
5COPY libhello.so ./
6
7RUN yum install -y libasan10
8
9ENTRYPOINT LD_PRELOAD=/lib64/libasan.so.6.0.0 java -cp jna-5.18.1.jar Test.java

amazoncorrectto:21-2023 基础镜像也行,安装 libasan 包的命令是 yum install -y libasan. 还有在配置 LD_PRELOAD 时要 使用实际的文件,不能是链接文件, /lib64/libasan.so.6 是无效的.

构建并启动

1docker build -t java-web .
2docker run -it -p 8080:8080 java-web

访问 http://localhost:8080/fail 接口后,能看到更详细的错误信息

在 Docker 容器即服务端控制台会输出

 1[root@lin-0aff3de6 ~]# docker run -it -p 8080:8080 java-web
 2loaded shared library
 3Server started on port 8080
 4hello devil
 5buf: you are devil!
 6=================================================================
 7==1==ERROR: AddressSanitizer: attempting double-free on 0x606000087ec0 in thread T25 (HTTP-Dispatcher):
 8    #0 0x7f0b23be4617 in operator delete[](void*) (/lib64/libasan.so.6.0.0+0xb2617)
 9    #1 0x7f0a3e0cc273 in sayHello /root/hello.cpp:17
10    #2 0x7f0a3e17f051  (/root/.cache/JNA/temp/jna6772144201813723775.tmp+0x16051)
11    #3 0x7f0a3e17e1cb  (/root/.cache/JNA/temp/jna6772144201813723775.tmp+0x151cb)
12    #4 0x7f0a3e17e507  (/root/.cache/JNA/temp/jna6772144201813723775.tmp+0x15507)
13    #5 0x7f0a3e173a59  (/root/.cache/JNA/temp/jna6772144201813723775.tmp+0xaa59)
14    #6 0x7f0a3e173f20  (/root/.cache/JNA/temp/jna6772144201813723775.tmp+0xaf20)
15    #7 0x7f0b0cea19bf  (<unknown module>)
16
170x606000087ec0 is located 0 bytes inside of 64-byte region [0x606000087ec0,0x606000087f00)
18freed by thread T25 (HTTP-Dispatcher) here:
19    #0 0x7f0b23be4617 in operator delete[](void*) (/lib64/libasan.so.6.0.0+0xb2617)
20    #1 0x7f0a3e0cc260 in sayHello /root/hello.cpp:16
21    #2 0x7f0a3e17f051  (/root/.cache/JNA/temp/jna6772144201813723775.tmp+0x16051)
22    #3 0x7f0a3dc33faf  (<unknown module>)
23
24previously allocated by thread T25 (HTTP-Dispatcher) here:
25    #0 0x7f0b23be3b17 in operator new[](unsigned long) (/lib64/libasan.so.6.0.0+0xb1b17)
26    #1 0x7f0a3e0cc213 in sayHello /root/hello.cpp:11
27    #2 0x7f0a3e17f051  (/root/.cache/JNA/temp/jna6772144201813723775.tmp+0x16051)
28    #3 0x7f0a3dc33faf  (<unknown module>)
29
30Thread T25 (HTTP-Dispatcher) created by T1 here:
31    #0 0x7f0b23b8a1a1 in pthread_create (/lib64/libasan.so.6.0.0+0x581a1)
32    #1 0x7f0b1e532112 in os::create_thread(Thread*, os::ThreadType, unsigned long) (/usr/lib/jvm/java-21-amazon-corretto/lib/server/libjvm.so+0xcff112)
33    #2 0x7f0b1e226d81 in JVM_StartThread (/usr/lib/jvm/java-21-amazon-corretto/lib/server/libjvm.so+0x9f3d81)
34    #3 0x7f0b0cea19bf  (<unknown module>)
35    #4 0x7f0b0ce9d17f  (<unknown module>)
36    #5 0x7f0b0ce9d17f  (<unknown module>)
37    #6 0x7f0b0ce9d17f  (<unknown module>)
38    #7 0x7f0b0ce9d17f  (<unknown module>)
39    #8 0x7f0b0ce9d17f  (<unknown module>)
40    #9 0x7f0b0ce9d17f  (<unknown module>)
41    #10 0x7f0b0ce9d2f1  (<unknown module>)
42    #11 0x7f0b0ce9d2f1  (<unknown module>)
43    #12 0x7f0b0ce9d2f1  (<unknown module>)
44    #13 0x7f0b0ce9d797  (<unknown module>)
45    #14 0x7f0b0ce9d2f1  (<unknown module>)
46    #15 0x7f0b0ce9d17f  (<unknown module>)
47    #16 0x7f0b0ce9d17f  (<unknown module>)
48    #17 0x7f0b0ce95cc5  (<unknown module>)
49    #18 0x7f0b1e149363 in JavaCalls::call_helper(JavaValue*, methodHandle const&, JavaCallArguments*, JavaThread*) (/usr/lib/jvm/java-21-amazon-corretto/lib/server/libjvm.so+0x916363)
50    #19 0x7f0b1e1f3cae in jni_invoke_static(JNIEnv_*, JavaValue*, _jobject*, JNICallType, _jmethodID*, JNI_ArgumentPusher*, JavaThread*) [clone .isra.156] [clone .constprop.261] (/usr/lib/jvm/java-21-amazon-corretto/lib/server/libjvm.so+0x9c0cae)
51    #20 0x7f0b1e1f5ede in jni_CallStaticVoidMethod (/usr/lib/jvm/java-21-amazon-corretto/lib/server/libjvm.so+0x9c2ede)
52    #21 0x7f0b23711736 in JavaMain (/usr/lib/jvm/java-21-amazon-corretto/bin/../lib/libjli.so+0x5736)
53    #22 0x7f0b23714528 in ThreadJavaMain (/usr/lib/jvm/java-21-amazon-corretto/bin/../lib/libjli.so+0x8528)
54    #23 0x7f0b234f544a in start_thread (/lib64/libpthread.so.0+0x744a)
55
56Thread T1 created by T0 here:
57    #0 0x7f0b23b8a1a1 in pthread_create (/lib64/libasan.so.6.0.0+0x581a1)
58    #1 0x7f0b237150cc in CallJavaMainInNewThread (/usr/lib/jvm/java-21-amazon-corretto/bin/../lib/libjli.so+0x90cc)
59    #2 0x7f0b2371212c in ContinueInNewThread (/usr/lib/jvm/java-21-amazon-corretto/bin/../lib/libjli.so+0x612c)
60    #3 0x7f0b23712b7e in JLI_Launch (/usr/lib/jvm/java-21-amazon-corretto/bin/../lib/libjli.so+0x6b7e)
61    #4 0x557760e00a4d in main (/usr/lib/jvm/java-21-amazon-corretto/bin/java+0xa4d)
62    #5 0x7f0b22f5e139 in __libc_start_main (/lib64/libc.so.6+0x21139)
63
64SUMMARY: AddressSanitizer: double-free (/lib64/libasan.so.6.0.0+0xb2617) in operator delete[](void*)
65==1==ABORTING

这个非常有用. 编译动态库时还要加上 -g 参数, 这样在输出的错误信息中就能看到具体的行号了. 对于优化级别 -O, 0, 1, 2, 3 就看行号准不准确了。

总结

使用 Docker 容器运行 Java 应用时,触发 C++ 动态库崩溃后,无论是非用 systemd-coredump 管理 core dump 都未能成功生成 core dump 文件, 不过实际的一个 SpringBoot Web 应用确实在所用 C++ 代码崩溃后由 systemd-coredump 生成了 core dump 文件,尚不知问题出在哪里.

在此就不在测试 SpringBoot Web 应用了。

最后用,但可能性能会下降一半的方式就是用 -fsanitize=address -g 编译 C++ 代码,虽然不能生成 core dump 文件,但能在控制台输出更详细的错误信息了。

永久链接 https://yanbin.blog/docker-java-cpp-dll-core-dump/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。