容器内64位Linux应用线程定位求助(无CAP_SYS_PTRACE权限)
既然你已经列出了所有线程,又没法用gdb附加,咱们可以完全依赖Linux内核暴露的proc文件系统来获取线程的执行位置信息——这不需要任何ptrace权限,是容器环境下的绝佳替代方案。
以下是几个实用的方法:
直接读取线程调用栈
每个线程在/proc/<进程PID>/task/<线程TID>目录下都有一个stack文件,里面记录了线程当前的调用栈(内核态+用户态)。只需遍历所有线程读取这个文件即可:# 先替换成你的目标进程PID TARGET_PID=123 # 遍历所有线程ID for TID in $(ls /proc/$TARGET_PID/task/); do echo "=== 线程 $TID 调用栈 ===" cat /proc/$TARGET_PID/task/$TID/stack echo -e "\n" done要是
stack文件为空,大概率是内核没开CONFIG_STACKTRACE配置,但多数情况下你能拿到用户态的栈帧,直接看到线程卡在哪个函数里。解析程序计数器(PC)定位执行位置
/proc/<进程PID>/task/<线程TID>/stat的第28个字段是线程当前的程序计数器(PC)值,也就是当前执行指令的地址。结合应用的符号表,就能反推出对应的函数:# 替换成你的进程PID和目标线程TID PID=123 TID=456 # 获取PC地址 PC_ADDR=$(awk '{print $28}' /proc/$PID/task/$TID/stat) # 用addr2line解析地址到函数(需要安装binutils) addr2line -e /path/to/your/application $PC_ADDR提示:如果应用没带符号表,你可以先从
/proc/$PID/maps里找到可执行段的起始基地址,用PC地址减去基地址得到相对偏移,再和编译时生成的反汇编代码对比,也能定位到大致位置。另外ASLR不影响这里,因为proc里的地址都是运行时的实际加载地址。查看线程状态与内存映射
/proc/<进程PID>/task/<线程TID>/status里的State字段能告诉你线程当前的状态(比如是否处于睡眠、阻塞等),配合/proc/<进程PID>/task/<线程TID>/maps可以确认线程当前的内存上下文,判断是在用户态代码段、内核态还是某个动态库中执行。试试容器内的
pstack工具
Ubuntu 16.04里的pstack有些版本是基于proc文件系统实现的,不需要ptrace权限,可以直接尝试:pstack <目标进程PID>如果提示权限不足,就回到前面的
proc文件系统方法就行。
额外提醒:如果容器里没有addr2line,可以用apt-get update && apt-get install -y binutils安装(前提是容器有网络权限)。
内容的提问来源于stack exchange,提问作者Gyorgy Szekely

