Reverse debugging кода на C/C++ с помощью GDB и RR

20 июля 2016

Типичный отладчик, такой как WinDbg или LLDB, позволяет выполнять программу шаг за шагом, просматривая значения переменных, создавая брейкпоинты, и так далее. Reverse debugging — это когда вы можете делать все то же самое, но не в прямом порядке, а в обратном, как бы «назад во времени». Сегодня мы познакомимся с двумя способами reverse debugging’а в Linux. Вероятно, эти способы работают и на других платформах, но я не проверял.

Важно! Как всегда, когда собираетесь отлаживать программу, убедитесь, что она была собрана с флагами -O0 -g.

«Обычный» reverse debugging с GDB

В отладчике GDB есть команда record. После ее выполнения отладчик начинает записывать все действия, совершаемые программой. GDB может обращать записанные действия, создавая тем самым иллюзию выполнения программы назад во времени. Основные команды следующие.

Как уже отмечалось, начать запись:

record

Как step, next, и прочие, только с движением в обратную сторону (также можно сокращать до rs, rn, rf и rc соответственно):

reverse-step
reverse-next
reverse-finish
reverse-continue

Переключить текущее направление выполнения программы на прямое или обратное. Команды step, next и прочие выполняются в этом направлении:

set exec-direction forward
set exec-direction reverse

Отключить hardware watchpoints. Это необходимо, если вы хотите использовать watchpoints при выполнении программы в обратную сторону:

set can-use-hw-watchpoints 0

По большому счет, это все. Больше не нужно распихивать по коду отладочный вывод, если хочется посмотреть, что программа делала в прошлом!

«Продвинутый» reverse debugging с RR

Представьте, что вы работаете в Mozilla. У вас есть какой-то continuous integration, который собирает Firefox и гоняет на нем тесты. Иногда Firefox падает. В этом случае вы можете проанализировать корку, но посмотреть, что программа делала в прошлом, с ее помощью вы не можете. Что намного хуже, иногда какой-то тест не проходит, но Firefox при этом не падает. В этом случае у вас нет даже корки.

Для решения описанной проблемы парни из Mozilla запилили RR. Данный инструмент записывает, что и как делала программа, а также предоставляет возможность проиграть записанные действия в отладчике, как в прямом, так и в обратном порядке. Таким образом, мы получаем не только reverse debugging, но и возможность отлаживать трудновоспроизводимые баги, если их удается воспроизвести хотя бы один раз. К тому же, RR работает намного быстрее record в GDB.

Для установки RR качаем с GitHub пакет, соответствующий вашей архитектуре.

Ставим:

sudo dpkg -i rr-4.3.0-Linux-x86_64.deb

Также, если у вас ноутбук, рекомендуется выполнить команды:

sudo apt-get install cpufrequtils
sudo cpufreq-set -g performance

Иначе, если ноутбук разряжается, производительность RR может упасть в два раза. Примите во внимание, что эта настройка держится до ребута. Вообще, утилиты cpufreq-* очень полезны. Например, с их помощью можно заставить ноутбук поменьше греться.

Используется RR как-то так:

rr record ./myprog --arg1 --arg2
rr replay

Во время записи RR умеет рандомизировать решения скедулера, что упрощает отладку некоторых трудно воспроизводимых багов:

rr record --chaos ./myprog --arg1 --arg2

Записи хранятся в ~/.local/share/rr/, можно воспроизвести не только последнюю запись выполнения программы:

rr replay ~/.local/share/rr/myprog-0

После выполнения rr replay мы попадаем в GDB, где нам доступны все те же команды rc, rn, и прочие, что были рассмотрены выше. Вот, собственно, и все!

Заключение

GDB и RR — инструменты в чем-то похожие, но немного для разных задач. Например, серьезное ограничение RR заключается в том, что сейчас он не умеет цепляться к работающему процессу по его PID. GDB лишен этого недостатка, но и работает медленнее. Вместе GDB и RR должны справляться практически с любыми ситуациями.

Дополнение: Возможность reverse debugging также есть в Radare2.

Метки: , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.