Отладка основных файлов, сгенерированных на коробке клиента
Мы получаем основные файлы от запуска нашего программного обеспечения на коробке клиента. К сожалению, потому что мы всегда компилировали с -O2 без отладочных символов, это привело к ситуациям, когда мы не могли понять, почему это вылетало, мы изменили сборки так, что теперь они генерируют -g и -O2 вместе. Затем мы советуем клиенту запустить двоичный файл -g, чтобы его было легче отлаживать.
У меня есть несколько вопросов:
- Что происходит, когда файл ядра создается из дистрибутива Linux, отличного от того, который мы используем в Dev? Является ли трассировка стека вообще значимой?
- Есть ли хорошие книги для отладки на Linux или Solaris? Что-то, ориентированное на пример, было бы здорово. Я ищу примеры из реальной жизни, чтобы выяснить, почему рутина рухнула и как автор пришел к решению. Было бы неплохо что-то большее на уровне от среднего до продвинутого, так как я занимаюсь этим уже некоторое время. Некоторая сборка тоже подойдет.
Вот пример сбоя, который требует, чтобы мы сказали Клиенту получить -g вер. двоичного файла:
Program terminated with signal 11, Segmentation fault.
#0 0xffffe410 in __kernel_vsyscall ()
(gdb) where
#0 0xffffe410 in __kernel_vsyscall ()
#1 0x00454ff1 in select () from /lib/libc.so.6
...
<omitted frames>
В идеале я хотел бы выяснить, почему именно приложение зависло - я подозреваю, что это повреждение памяти, но я не уверен на 100%.
Удаленная отладка строго запрещена.
Спасибо
5 ответов
Что происходит, когда файл ядра создается из дистрибутива Linux, отличного от того, который мы используем в Dev? Является ли трассировка стека вообще значимой?
Если исполняемый файл динамически связан, как и ваш, стек, создаваемый GDB, (скорее всего) не будет иметь смысла.
Причина: GDB знает, что ваш исполняемый файл потерпел крах, вызвав что-то в libc.so.6
по адресу 0x00454ff1
, но он не знает, какой код был по этому адресу. Так это выглядит в вашей копии libc.so.6
и обнаруживает, что это в select
так что печатает это.
Но шансы, что 0x00454ff1
также в избранном в ваших клиентов копию libc.so.6
довольно маленькие. Скорее всего, у клиента была другая процедура по этому адресу, возможно, abort
,
Ты можешь использовать disas select
и заметьте, что 0x00454ff1
либо находится в середине инструкции, либо предыдущая инструкция не является CALL
, Если что-то из этого верно, ваша трассировка стека не имеет смысла.
Однако вы можете помочь себе: вам просто нужно получить копию всех библиотек, перечисленных в (gdb) info shared
из системы клиента. Попросите клиента сменить их, например, на
cd /
tar cvzf to-you.tar.gz lib/libc.so.6 lib/ld-linux.so.2 ...
Затем в вашей системе:
mkdir /tmp/from-customer
tar xzf to-you.tar.gz -C /tmp/from-customer
gdb /path/to/binary
(gdb) set solib-absolute-prefix /tmp/from-customer
(gdb) core core # Note: very important to set solib-... before loading core
(gdb) where # Get meaningful stack trace!
Затем мы советуем клиенту запустить двоичный файл -g, чтобы его было легче отлаживать.
Гораздо лучший подход:
- строить с
-g -O2 -o myexe.dbg
cp myexe.dbg myexe
strip -g myexe
- распространять
myexe
клиентам - когда клиент получает
core
использоватьmyexe.dbg
отладить это
Вы будете иметь полную символическую информацию (файл / строка, локальные переменные), без необходимости отправлять специальный двоичный файл клиенту и не раскрывая слишком много деталей о ваших источниках.
Вы действительно можете получить полезную информацию из аварийного дампа, даже из оптимизированной компиляции (хотя это, технически, называется "основной болью в заднице"). -g
Компиляция действительно лучше, и да, вы можете сделать это, даже если машина, на которой произошел дамп, является другим дистрибутивом. По сути, с одним предупреждением вся важная информация содержится в исполняемом файле и попадает в дамп.
Когда вы сопоставите файл ядра с исполняемым файлом, отладчик сможет сообщить вам, где произошел сбой, и показать вам стек. Это само по себе должно помочь. Вы также должны узнать как можно больше о ситуации, в которой это происходит, - могут ли они надежно воспроизвести ее? Если да, можете ли вы воспроизвести его?
Теперь, вот предостережение: место, где понятие "все есть" разрушается, - это общие объектные файлы, .so
файлы. Если это не удается из-за проблем с ними, у вас не будет нужных вам таблиц символов; вы можете только увидеть, какая библиотека .so
это происходит в.
Есть много книг об отладке, но я не могу придумать одну, которую я бы порекомендовал.
Копирование резолюции из моего вопроса, который посчитали дубликатом этого.
set solib-absolute-prefix
из принятого решения мне не помогло.
set sysroot
было абсолютно необходимо, чтобы gdb загружал локально предоставленные библиотеки. Вот список команд, которые я использовал для открытия дампа ядра:
# note: all the .so files obtained from user machine must be put into local directory.
#
# most importantly, the following files are necessary:
# 1. libthread_db.so.1 and libpthread.so.0: required for thread debugging.
# 2. other .so files are required if they occur in call stack.
#
# these files must also be renamed exactly as the symlinks
# i.e. libpthread-2.28.so should be renamed to libpthread.so.0
# load executable file
file ./thedarkmod.x64
# force gdb to forget about local system!
# load all .so files using local directory as root
set sysroot .
# drop dump-recorded paths to .so files
# i.e. load ./libpthread.so.0 instead of ./lib/x86_64-linux-gnu/libpthread.so.0
set solib-search-path .
# disable damn security protection
set auto-load safe-path /
# load core dump file
core core.6487
# print stacktrace
bt
Проверьте значения локальных переменных, которые вы видите, когда вы ходите по стеку? Особенно вокруг вызова select(). Сделайте это на коробке клиента, просто загрузите дамп и пройдитесь по стеку...
Также проверьте значение FD_SETSIZE на платформах DEV и PROD!
Насколько я помню, вам не нужно просить клиента запускать двоичный файл с опцией -g. Что вам нужно, так это то, что у вас должна быть сборка с опцией -g. При этом вы можете загрузить файл ядра, и он покажет всю трассировку стека. Я помню несколько недель назад, я создал файлы ядра, со сборкой (-g) и без -g, и размер ядра был таким же.