HPUX 上可用的 GDB 版本有一个名为“packcore”的命令,它创建一个包含核心转储、可执行文件和所有库的 tarball。当尝试在不同的机器上调试核心转储时,我发现这非常有用。
我可能在 Linux 机器上找到的标准版 GDB 中是否有类似的命令?
我正在寻找一个简单的命令,当生产机器上出现问题时,不一定是开发人员的人可以运行该命令。
核心文件包括生成它的命令。理想情况下,这将包括适当可执行文件的完整路径。例如:
$ file core.29529
core.29529: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from '/bin/sleep 60'
在 ELF 二进制文件上运行ldd
将显示它依赖的库:
$ ldd /bin/sleep
linux-vdso.so.1 => (0x00007fff1d3ff000)
libc.so.6 => /lib64/libc.so.6 (0x0000003d3ce00000)
/lib64/ld-linux-x86-64.so.2 (0x0000003d3ca00000)
所以现在我知道了分析核心转储所需的可执行文件和库。
这里棘手的部分是从核心文件中提取可执行路径。似乎没有直接阅读此内容的好工具。数据以 prpsinfo 结构编码(来自/usr/include/sys/procfs.h
),您可以使用以下方法找到数据的位置大小readelf
:
$ readelf -n core.29529
Notes at offset 0x00000468 with length 0x00000558:
Owner Data size Description
CORE 0x00000150 NT_PRSTATUS (prstatus structure)
CORE 0x00000088 NT_PRPSINFO (prpsinfo structure)
CORE 0x00000130 NT_AUXV (auxiliary vector)
CORE 0x00000200 NT_FPREGSET (floating point registers)
...所以理论上可以编写一个代码片段来从这个结构中提取命令行并以一种使整个过程更容易自动化的方式打印出来。当然,您可以只解析以下输出file
:
$ file core.29529 | sed "s/.*from '\([^']*\)'/\1/"
/bin/sleep 60
这就是所有的部分。这是将所有内容放在一起的起点:
#!/bin/sh
core=$1
exe=$(file $core | sed "s/.*from '\([^']*\)'/\1/" | awk '{print $1}')
libs=$(
ldd $exe |
awk '
/=> \// {print $3}
! /=>/ {print $1}
'
)
cat <<EOF | tar -cah -T- -f $1-all.tar.xz
$libs
$exe
EOF
对于我的示例,如果我命名此脚本packcore
并通过命令在核心文件上运行它sleep
,我会得到:
$ packcore core.29529
tar: Removing leading `/' from member names
$ tar -c -f core.29529-all.tar.xz
core.29529
lib64/libc.so.6
lib64/ld-linux-x86-64.so.2
bin/sleep
就目前而言,这个脚本非常脆弱。我ldd
仅基于此示例输出对输出做出了很多假设。
这是一个执行必要步骤的脚本(仅在 RHEL5 上测试过,但也可能在其他地方工作):
#!/bin/sh
#
# Take a core dump and create a tarball of all of the binaries and libraries
# that are needed to debug it.
#
include_core=1
keep_workdir=0
usage()
{
argv0="$1"
retval="$2"
errmsg="$3"
if [ ! -z "$errmsg" ] ; then
echo "ERROR: $errmsg" 1>&2
fi
cat <<EOF
Usage: $argv0 [-k] [-x] <corefile>
Parse a core dump and create a tarball with all binaries and libraries
needed to be able to debug the core dump.
Creates <corefile>.tgz
-k - Keep temporary working directory
-x - Exclude the core dump from the generated tarball
EOF
exit $retval
}
while [ $# -gt 0 ] ; do
case "$1" in
-k)
keep_workdir=1
;;
-x)
include_core=0
;;
-h|--help)
usage "$0" 0
;;
-*)
usage "$0" 1 "Unknown command line arguments: $*"
;;
*)
break
;;
esac
shift
done
COREFILE="$1"
if [ ! -e "$COREFILE" ] ; then
usage "$0" 1 "core dump '$COREFILE' doesn't exist."
fi
case "$(file "$COREFILE")" in
*"core file"*)
break
;;
*)
usage "$0" 1 "per the 'file' command, core dump '$COREFILE' is not a core dump."
;;
esac
cmdname=$(file "$COREFILE" | sed -e"s/.*from '\(.*\)'/\1/")
echo "Command name from core file: $cmdname"
fullpath=$(which "$cmdname")
if [ ! -x "$fullpath" ] ; then
usage "$0" 1 "unable to find command '$cmdname'"
fi
echo "Full path to executable: $fullpath"
mkdir "${COREFILE}.pack"
gdb --eval-command="quit" "${fullpath}" ${COREFILE} 2>&1 | \
grep "Reading symbols" | \
sed -e's/Reading symbols from //' -e's/\.\.\..*//' | \
tar --files-from=- -cf - | (cd "${COREFILE}.pack" && tar xf -)
if [ $include_core -eq 1 ] ; then
cp "${COREFILE}" "${COREFILE}.pack"
fi
tar czf "${COREFILE}.pack.tgz" "${COREFILE}.pack"
if [ $keep_workdir -eq 0 ] ; then
rm -r "${COREFILE}.pack"
fi
echo "Done, created ${COREFILE}.path.tgz"
我为此编写了shell 脚本。它使用上述答案中的想法,并添加一些使用信息和附加命令。将来我可能会在带有 gdb 的 docker 容器中添加用于快速调试的命令。