linux 下利用gdb + bash 实现类似gflags的功能
时间:2010-05-28 来源:slimzhao
在真实的程序执行环境下, 一个进程可能不能通过gdb filename的方式来调试, 原因:
1. 父进程为它准备了socket
2. 你进程打开了一些文件.
所有父进程为它进行的积累, 在直接运行程序时不会出现.
所以, 需要有一种方法, 在另一程序fork / execl一个新程序时, 有机会在该程序的最开始比如main断住.
假设程序为test2,
mv test2 test2.real
cat > test2 <<END
#!/bin/bash
while [ -z "$GDB_READY" ] ; do
echo sleep 1 second...
export FORCE_REFRESH_ENV=1
sleep 1
export -n FORCE_REFRESH_ENV
done
exec ./test2.real "$@"
END
这段脚本偷梁换柱取代了原来的ELF格式可执行文件, 在它被执行时, 不停地循环, 循环的条件是检测一个叫
GDB_READY的环境变量.
这样, 在该程序被调用时, 相当于阻塞在了exec调用上, 原因是该环境变量不存在.
在另一终端中, 用ps ax | grep test2 找到PID,
gdb -p PID
调试该bash 进程. 通过
p setenv("GDB_READY", "1")
为该进程设置环境变量, 从而退出上面的while循环, 在设置之后, 先不能急于continue, 因为断点还没设置.
symbol-file ./test2.real -readnow
可以在尚未load该进程时, 提前告之gdb,
然后可以:
b test2.c:main
continue
会在该进程的main处断住.
bash脚本中FORCE_REFRESH_ENV 是最让人迷惑的地方, 如果没有export + export -n, 整个方案就不行, 这是因为bash会对环境变量作一个cache, 这两个动作保证能引发它刷新该cache, 这样下一次循环时才能判断出变量GDB_READY 是否真正设置了.
bash对 $GDB_READY的实现并不是直接调用 getenv, 而是在一个内部的hash中去找.
1. 父进程为它准备了socket
2. 你进程打开了一些文件.
所有父进程为它进行的积累, 在直接运行程序时不会出现.
所以, 需要有一种方法, 在另一程序fork / execl一个新程序时, 有机会在该程序的最开始比如main断住.
假设程序为test2,
mv test2 test2.real
cat > test2 <<END
#!/bin/bash
while [ -z "$GDB_READY" ] ; do
echo sleep 1 second...
export FORCE_REFRESH_ENV=1
sleep 1
export -n FORCE_REFRESH_ENV
done
exec ./test2.real "$@"
END
这段脚本偷梁换柱取代了原来的ELF格式可执行文件, 在它被执行时, 不停地循环, 循环的条件是检测一个叫
GDB_READY的环境变量.
这样, 在该程序被调用时, 相当于阻塞在了exec调用上, 原因是该环境变量不存在.
在另一终端中, 用ps ax | grep test2 找到PID,
gdb -p PID
调试该bash 进程. 通过
p setenv("GDB_READY", "1")
为该进程设置环境变量, 从而退出上面的while循环, 在设置之后, 先不能急于continue, 因为断点还没设置.
symbol-file ./test2.real -readnow
可以在尚未load该进程时, 提前告之gdb,
然后可以:
b test2.c:main
continue
会在该进程的main处断住.
bash脚本中FORCE_REFRESH_ENV 是最让人迷惑的地方, 如果没有export + export -n, 整个方案就不行, 这是因为bash会对环境变量作一个cache, 这两个动作保证能引发它刷新该cache, 这样下一次循环时才能判断出变量GDB_READY 是否真正设置了.
bash对 $GDB_READY的实现并不是直接调用 getenv, 而是在一个内部的hash中去找.
相关阅读 更多 +