如何让Intel PIN与Java服务器(AcmeAir)协同工作?
Intel PIN与Docker中AcmeAir Java应用协同问题解决方案
问题背景
我正在获取AcmeAir单体Java应用的CPU占用情况,该应用已在Docker容器中完成部署(含MongoDB数据库),可正常运行。容器内Java服务器的正常启动命令为:
/opt/java/openjdk/bin/java -javaagent:/opt/ol/wlp/bin/tools/ws-javaagent.jar -Djava.awt.headless=true -Djdk.attach.allowAttachSelf=true --add-exports java.base/sun.security.action=ALL-UNNAMED --add-exports java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports java.naming/com.sun.jndi.url.ldap=ALL-UNNAMED --add-exports jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED --add-exports java.security.jgss/sun.security.krb5.internal=ALL-UNNAMED --add-exports jdk.attach/sun.tools.attach=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.naming/javax.naming.spi=ALL-UNNAMED --add-opens jdk.naming.rmi/com.sun.jndi.url.rmi=ALL-UNNAMED --add-opens java.naming/javax.naming=ALL-UNNAMED --add-opens java.rmi/java.rmi=ALL-UNNAMED --add-opens java.sql/java.sql=ALL-UNNAMED --add-opens java.management/javax.management=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.desktop/java.awt.image=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.base/sun.net.www.protocol.https=ALL-UNNAMED --add-exports jdk.management.agent/jdk.internal.agent=ALL-UNNAMED --add-exports java.base/jdk.internal.vm=ALL-UNNAMED -jar /opt/ol/wlp/bin/tools/ws-server.jar defaultServer
尝试用Intel PIN追踪该进程时遇到以下问题:
- 将PIN设为入口点:通过
docker-compose.override.yml覆盖入口点,用自定义脚本启动,配置如下:
acmeair-db: container_name: acmeair-db image: mongo:4.0.0 net: ${NETWORK} privileged: true acmeair-monolithic-java: container_name: acmeair-monolithic-java dockerfile: Dockerfile net: ${NETWORK} entrypoint: ./startup-pin.sh build: . ports: - "80:9080" environment: - MONGO_HOST=acmeair-db volumes_from: - acmeair-db mem_limit: 1024m privileged: true
启动脚本内容:
#!/bin/bash /pin-gcc-linux/pin -t /pin-gcc-linux/source/tools/ManualExamples/obj-intel64/proccount.so -- /opt/java/openjdk/bin/java -javaagent:/opt/ol/wlp/bin/tools/ws-javaagent.jar -Djava.awt.headless=true -Djdk.attach.allowAttachSelf=true --add-exports java.base/sun.security.action=ALL-UNNAMED --add-exports java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-exports java.naming/com.sun.jndi.url.ldap=ALL-UNNAMED --add-exports jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED --add-exports java.security.jgss/sun.security.krb5.internal=ALL-UNNAMED --add-exports jdk.attach/sun.tools.attach=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.naming/javax.naming.spi=ALL-UNNAMED --add-opens jdk.naming.rmi/com.sun.jndi.url.rmi=ALL-UNNAMED --add-opens java.naming/javax.naming=ALL-UNNAMED --add-opens java.rmi/java.rmi=ALL-UNNAMED --add-opens java.sql/java.sql=ALL-UNNAMED --add-opens java.management/javax.management=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.desktop/java.awt.image=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.base/sun.net.www.protocol.https=ALL-UNNAMED --add-exports jdk.management.agent/jdk.internal.agent=ALL-UNNAMED --add-exports java.base/jdk.internal.vm=ALL-UNNAMED -jar /opt/ol/wlp/bin/tools/ws-server.jar defaultServer
执行后虽有PIN追踪输出,但服务器多数服务异常,且无法访问服务器日志。
- 通过PID附着PIN:使用命令
/pin-gcc-linux/pin -pid <PID> -t /pin-gcc-linux/source/tools/ManualExamples/obj-intel64/proccount.so- 宿主机附着到容器进程时报错:
A: /tmp_proj/pinjen/workspace/pypl-pin-nightly/GitPin/Source/pin/base_l/sysfuncs_linux.cpp: GetProcessName: 107: assertion failed: p - 容器内附着时,进程无崩溃但停滞无输出。
- 宿主机附着到容器进程时报错:
解决方法与指导
1. 调整PIN启动参数,兼容Java JIT特性
Java是JIT编译语言,PIN默认对动态生成代码的处理会导致兼容性问题,需添加以下参数:
- 启动PIN时加入
-ifeellucky,绕过PIN对Java的部分兼容性检查 - 添加
-follow-childs,追踪Java进程的子线程(JIT编译的代码线程)
修改后的启动脚本命令:
/pin-gcc-linux/pin -ifeellucky -follow-childs -t /pin-gcc-linux/source/tools/ManualExamples/obj-intel64/proccount.so -- <原Java完整启动命令>
2. 容器内PID附着的正确操作
- 进入容器时需确保拥有进程追踪权限:
docker exec -it --cap-add=SYS_PTRACE acmeair-monolithic-java bash - 获取Java进程PID:
ps aux | grep java | grep -v grep | awk '{print $2}' - 执行PIN附着命令,同时指定输出文件避免拦截:
/pin-gcc-linux/pin -o pin_output.log -follow-childs -pid <Java_PID> -t /pin-gcc-linux/source/tools/ManualExamples/obj-intel64/proccount.so
3. 日志调试优化
- 修改
docker-compose.yml,挂载服务器日志目录到宿主机:acmeair-monolithic-java: # 保留原有配置 volumes: - ./server-logs:/opt/ol/wlp/usr/servers/defaultServer/logs - 启动脚本中添加日志重定向,分离PIN和Java输出:
#!/bin/bash chmod +x /pin-gcc-linux/pin chmod +x /pin-gcc-linux/source/tools/ManualExamples/obj-intel64/proccount.so /pin-gcc-linux/pin -ifeellucky -o pin_trace.log -follow-childs -t /pin-gcc-linux/source/tools/ManualExamples/obj-intel64/proccount.so -- /opt/java/openjdk/bin/java <原Java参数> > java_server.log 2>&1
4. 容器权限优化
- 移除
privileged: true,改用更精细的权限控制,添加进程追踪能力:acmeair-monolithic-java: # 保留原有配置 privileged: false cap_add: - SYS_PTRACE
内容的提问来源于stack exchange,提问作者De Santa Michell
相关产品推荐
相关产品推荐

