Павел Довгалюк, Обратная отладка
-
Upload
sergey-platonov -
Category
Software
-
view
432 -
download
1
Transcript of Павел Довгалюк, Обратная отладка
Обратная отладка
Довгалюк П. М.
Отладка
Отладка
Известно, что отладка в два раза сложнее написания программы. Поэтому если вы были предельно хитроумны при написании программы, то что же вы будете делать при ее отладке?
Брайан Керниган, Элементы стиля программирования
35-75% времени программиста уходит на отладку
Почему отладка сложна?
Недетерминировнные баги
Отладчик влияет на работу программы
Отладчик работает только в одном направлении
Не всегда удается передать сценарий воспроизведения ошибки от тестировщика к разработчику
Гейзенбаги
Для отладки программа запускается многократно
Ошибки в одних и тех же сценариях проявляются не всегда
Многопоточность
Случайные числа
Изменчивое окружение
Неинициализированные переменные
Недетерминированные ошибки
void do_nothing(int &a)
{
if (rand() == 42)
++a; // happy debugging
}
Еще более редко проявляющиеся ошибки DWORD WINAPI printThread(LPVOID lpParam)
{
while (TRUE)
if (var % 10000 == 0)
do_some_work(var);
return 0;
}
DWORD WINAPI incThread(LPVOID lpParam)
{
while (TRUE)
++var;
return 0;
}
Попробуем отладить с помощью printf DWORD WINAPI printThread(LPVOID lpParam)
{
while (TRUE)
if (var % 10000 == 0) {
printf("%d\n", var);
do_some_work(var);
}
return 0;
}
DWORD WINAPI incThread(LPVOID lpParam)
{
while (TRUE)
++var;
return 0;
}
Попробуем отладить с помощью printf DWORD WINAPI printThread(LPVOID lpParam)
{
while (TRUE)
if (var % 10000 == 0) {
do_some_work(var);
printf("%d\n", var);
}
return 0;
}
DWORD WINAPI incThread(LPVOID lpParam)
{
while (TRUE)
++var;
return 0;
}
Испорченные переменные #include <stdio.h>
void f(int *a) {
int i;
for (i = -1 ; i < 6 ; ++i)
a[i] = i;
}
int z = 1016;
int main() {
int a[5];
int *p = &z;
printf("%d\n", *p);
f(a);
printf("%d\n", *p);
}
Отладка сетевых протоколов
Приложение чувствительно ко времени
Удаленный сервер не знает, что идет отладка
Остановка программы приведет к таймауту соединения
Обратная отладка
Попытка «вернуться в прошлое» и узнать что произошло
Отладка с помощью трассировки
Нужно вставить много вызовов printf
Не всегда удобно обращаться к переменной (адрес неизвестен)
Замедляет работу
Может изменить результат работы
Нужно знать заранее что хочешь посмотреть
Требуется перекомпиляция
Требуется повторный запуск
Обратная отладка с помощью трассы инструкций
Для каждой инструкции сохранять изменившиеся ячейки
Можно перейти на любое расстояние вперед и назад перезаписывая и восстанавливая сохраненные данные
Работает медленно
Сохраняет очень много данных
Несколько килобайт на 1000 инструкций
Можно ли сохранять меньше данных?
int a, s = 0;
scanf(“%d”, &a);
for (int i = 0 ; i < a ; ++i)
s += i;
printf(“%d”, s);
Запись и воспроизведение
Обратная отладка с помощью воспроизведения
Ход выполнения программы в основном детерминирован
Можно сохранять только входные данные, информацию от планировщика потоков, прерывания
Сохраняется мало данных
Несколько байт на 1000 инструкций
Повторные выполнения всегда одинаковы
Быстро вернуться на произвольное расстояние назад нельзя
Отладка с воспроизведением
Сначала записываем работу программы
Для отладки работа воспроизводится
Можно «перемещаться во времени»
Отладчик
Отладчик
Результаты анализа
Фаза записи Фаза воспроизведения
Реальный мир
Пошаговое выполнение
Пошаговое выполнение
Пошаговое выполнение
Пошаговое выполнение
Обратная отладка
23
int *p = malloc(sizeof(int));
………….
p = NULL;
………….
int a = *p;
Обратная отладка
24
int *p = malloc(sizeof(int));
………….
p = NULL;
………….
int a = *p;
gdb> watch p
gdb> reverse-continue
Обратная отладка
25
int *p = malloc(sizeof(int));
………….
p = NULL;
………….
int a = *p;
gdb> watch p
gdb> reverse-continue
1 2 3 4
Команды обратной отладки
step
next
finish
continue
reverse step
reverse next
reverse finish
reverse continue
Запись и воспроизведение
Всё нужное для воспроизведения ошибки записывается
Обычно нельзя изменить ход выполнения программы во время воспроизведения
Записывать сценарий нужно начать заранее
Пошаговое перемещение назад работает медленно
Обратные отладчики native приложений
Отдельные приложения
GDB
Mozilla RR
TotalView
UndoDB
Виртуальные машины
QEMU
Wind River Simics
gdb
Версия 7+
Встроенная поддержка i386-linux, amd64-linux Буфер на 200000 инструкций
set record insn-number-max
Удаленная отладка (Simics, UndoDB и другие)
gdb
break
run
target record-full
continue
reverse-continue
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
long nr;
/*
* Determine whether and which event to report to ptracer. When
* called from kernel_thread or CLONE_UNTRACED is explicitly
* requested, no event is reported; otherwise, report if the event
* for the type of forking is enabled.
*/
if (!(clone_flags & CLONE_UNTRACED)) {
if (clone_flags & CLONE_VFORK)
trace = PTRACE_EVENT_VFORK;
else if ((clone_flags & CSIGNAL) != SIGCHLD)
trace = PTRACE_EVENT_CLONE;
else
trace = PTRACE_EVENT_FORK;
if (likely(!ptrace_event_enabled(current, trace)))
trace = 0;
}
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace);
/*
* Do this prior waking up the new thread - the thread pointer
* might get invalid after that point, if the thread exits quickly.
*/
if (!IS_ERR(p)) {
struct completion vfork;
struct pid *pid;
trace_sched_process_fork(current, p);
pid = get_task_pid(p, PIDTYPE_PID);
nr = pid_vnr(pid);
if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr);
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
get_task_struct(p);
}
wake_up_new_task(p);
/* forking complete and child started to run, tell ptracer */
if (unlikely(trace))
ptrace_event_pid(trace, pid);
if (clone_flags & CLONE_VFORK) {
if (!wait_for_vfork_done(p, &vfork))
ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
}
put_pid(pid);
} else {
nr = PTR_ERR(p);
}
return nr;
}
gdb
Повторные запуски программы могут размещаться по новым адресам
checkpoint – сохраняет снимок программы
restore id – восстанавливает указанный снимок
Каждый снимок представлен отдельным процессом в памяти
Позволяет исследовать разные ветки выполнения
gdb
Не отображает вывод на экран/консоль
Поддерживает только x86 под Linux
Длина буфера инструкций ограничена
Медленно работает gzip замедляется в 50000 раз
Mozilla RR
Используется для отладки Firefox
Записывает меньше данных, чем GDB
Сохраняются результаты выполнения системных вызовов, переключение потоков и т.п.
Работает относительно быстро
20-40% overhead при записи обычных приложений
В худшем случае ~300%
Mozilla RR
Записывает недетерминированные входы clock_gettime(...&now);
read(fd, buf, 4096);
__asm__("rdtsc")
ioctl(...)
UNIX signals...
Во время воспроизведения эмулирует эти вызовы
Не записывает процессы и потоки параллельно
Сериализует многопоточные приложения, чтобы всё записалось корректно
Каждому потоку по очереди выделяется интервал времени для работы
Mozilla RR
Автоматически запускает GDB при воспроизведении
Поддерживает команды обратной отладки
reverse-step
reverse-next
reverse-finish
reverse-continue
Поддерживает GDB checkpoint
Mozilla RR
По умолчанию сохраняет сценарий в файл
Поддерживает запись многопоточных приложений
Работает не на всех процессорах из-за использования аппаратных счетчиков
Поддержки ARM в ближайшее время не будет
Поддерживаются не все системные вызовы
Не воспроизводит графический интерфейс
QEMU
Эмулятор с открытым исходным кодом
Эмулирует x86, ARM, MIPS, PowerPC и др.
Отладка драйверов и операционных систем
38
Падение системы
Замедление работы
Изменение поведения из-за отладочных методов
Отладка ОС через gdbserver в симуляторе
39
Не нужно сохранять дампы, чтобы их анализировать
Можно подключиться в любой момент
Даже при критическом сбое
Даже до загрузки ОС
Отладка без реального оборудования
Отладка прошивки
Отладка моделей оборудования
Работает медленнее из-за виртуализации
Ход работы может измениться из-за остановок
Детерминированное воспроизведение и обратная отладка
40
Должна работать на всех платформах QEMU
Протестирована для x86, x64, ARM, MIPS
Отладка и анализ всей системы
Ядро и BIOS
Виртуальные устройства
Во время воспроизведения можно переключиться на обычную работу
Работает медленнее из-за виртуализации
QEMU
41
Опубликованы все патчи для обратной отладки
около 6000 LOC
В QEMU 2.5 включено ядро воспроизведения
Без блочных устройств, gdb, сети, USB
Патчи для дисковых устройств в работе
Почему же люди не используют обратную отладку? © Stackexchange, 2013
В GDB появилась в 2009 году
Historical debugging в MSVS
Нужен специальный отладчик
Работает медленно (в GDB)
Люди думают, что она не работает
Обратная отладка
Использование обратной отладки на 26% снижает время, затрачиваемое на поиск ошибок
Cambridge’s Judge Business School
Бесплатный отладчик Mozilla RR
Бесплатный отладчик GDB