Как легко упаковать библиотеки, необходимые для анализа дампа ядра (например, packcore) - PullRequest
2 голосов
/ 26 сентября 2011

Версия GDB, доступная в HPUX, имеет команду «packcore», которая создает тарбол, содержащий дамп ядра, исполняемый файл и все библиотеки. Я нашел это чрезвычайно полезным при попытке отладки дампов ядра на другом компьютере.

Есть ли в стандартной версии GDB аналогичная команда, которую я мог бы найти на компьютере с Linux?

Я ищу простую команду, которую может выполнить кто-то, кто не обязательно является разработчиком, когда на производственной машине дела идут плохо.

Ответы [ 2 ]

2 голосов
/ 26 сентября 2011

Файл ядра содержит команду, из которой он был создан. В идеале это будет включать полный путь к соответствующему исполняемому файлу. Например:

$ file core.29529 
core.29529: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from '/bin/sleep 60'

Запуск ldd в двоичном файле ELF покажет, от каких библиотек он зависит:

$ 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, основываясь только на этом примере вывода.

1 голос
/ 23 апреля 2013

Вот скрипт, который выполняет необходимые шаги (проверено только на 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"
...