Павел Довгалюк, Обратная отладка

43
Обратная отладка Довгалюк П. М.

Upload: sergey-platonov

Post on 16-Apr-2017

445 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Павел Довгалюк, Обратная отладка

Обратная отладка

Довгалюк П. М.

Page 2: Павел Довгалюк, Обратная отладка

Отладка

Page 3: Павел Довгалюк, Обратная отладка

Отладка

Известно, что отладка в два раза сложнее написания программы. Поэтому если вы были предельно хитроумны при написании программы, то что же вы будете делать при ее отладке?

Брайан Керниган, Элементы стиля программирования

35-75% времени программиста уходит на отладку

Page 4: Павел Довгалюк, Обратная отладка

Почему отладка сложна?

Недетерминировнные баги

Отладчик влияет на работу программы

Отладчик работает только в одном направлении

Не всегда удается передать сценарий воспроизведения ошибки от тестировщика к разработчику

Page 5: Павел Довгалюк, Обратная отладка

Гейзенбаги

Для отладки программа запускается многократно

Ошибки в одних и тех же сценариях проявляются не всегда

Многопоточность

Случайные числа

Изменчивое окружение

Неинициализированные переменные

Page 6: Павел Довгалюк, Обратная отладка

Недетерминированные ошибки

void do_nothing(int &a)

{

if (rand() == 42)

++a; // happy debugging

}

Page 7: Павел Довгалюк, Обратная отладка

Еще более редко проявляющиеся ошибки 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;

}

Page 8: Павел Довгалюк, Обратная отладка

Попробуем отладить с помощью 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;

}

Page 9: Павел Довгалюк, Обратная отладка

Попробуем отладить с помощью 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;

}

Page 10: Павел Довгалюк, Обратная отладка

Испорченные переменные #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);

}

Page 11: Павел Довгалюк, Обратная отладка

Отладка сетевых протоколов

Приложение чувствительно ко времени

Удаленный сервер не знает, что идет отладка

Остановка программы приведет к таймауту соединения

Page 12: Павел Довгалюк, Обратная отладка

Обратная отладка

Попытка «вернуться в прошлое» и узнать что произошло

Page 13: Павел Довгалюк, Обратная отладка

Отладка с помощью трассировки

Нужно вставить много вызовов printf

Не всегда удобно обращаться к переменной (адрес неизвестен)

Замедляет работу

Может изменить результат работы

Нужно знать заранее что хочешь посмотреть

Требуется перекомпиляция

Требуется повторный запуск

Page 14: Павел Довгалюк, Обратная отладка

Обратная отладка с помощью трассы инструкций

Для каждой инструкции сохранять изменившиеся ячейки

Можно перейти на любое расстояние вперед и назад перезаписывая и восстанавливая сохраненные данные

Работает медленно

Сохраняет очень много данных

Несколько килобайт на 1000 инструкций

Page 15: Павел Довгалюк, Обратная отладка

Можно ли сохранять меньше данных?

int a, s = 0;

scanf(“%d”, &a);

for (int i = 0 ; i < a ; ++i)

s += i;

printf(“%d”, s);

Page 16: Павел Довгалюк, Обратная отладка

Запись и воспроизведение

Page 17: Павел Довгалюк, Обратная отладка

Обратная отладка с помощью воспроизведения

Ход выполнения программы в основном детерминирован

Можно сохранять только входные данные, информацию от планировщика потоков, прерывания

Сохраняется мало данных

Несколько байт на 1000 инструкций

Повторные выполнения всегда одинаковы

Быстро вернуться на произвольное расстояние назад нельзя

Page 18: Павел Довгалюк, Обратная отладка

Отладка с воспроизведением

Сначала записываем работу программы

Для отладки работа воспроизводится

Можно «перемещаться во времени»

Отладчик

Отладчик

Результаты анализа

Фаза записи Фаза воспроизведения

Реальный мир

Page 19: Павел Довгалюк, Обратная отладка

Пошаговое выполнение

Page 20: Павел Довгалюк, Обратная отладка

Пошаговое выполнение

Page 21: Павел Довгалюк, Обратная отладка

Пошаговое выполнение

Page 22: Павел Довгалюк, Обратная отладка

Пошаговое выполнение

Page 23: Павел Довгалюк, Обратная отладка

Обратная отладка

23

int *p = malloc(sizeof(int));

………….

p = NULL;

………….

int a = *p;

Page 24: Павел Довгалюк, Обратная отладка

Обратная отладка

24

int *p = malloc(sizeof(int));

………….

p = NULL;

………….

int a = *p;

gdb> watch p

gdb> reverse-continue

Page 25: Павел Довгалюк, Обратная отладка

Обратная отладка

25

int *p = malloc(sizeof(int));

………….

p = NULL;

………….

int a = *p;

gdb> watch p

gdb> reverse-continue

1 2 3 4

Page 26: Павел Довгалюк, Обратная отладка

Команды обратной отладки

step

next

finish

continue

reverse step

reverse next

reverse finish

reverse continue

Page 27: Павел Довгалюк, Обратная отладка

Запись и воспроизведение

Всё нужное для воспроизведения ошибки записывается

Обычно нельзя изменить ход выполнения программы во время воспроизведения

Записывать сценарий нужно начать заранее

Пошаговое перемещение назад работает медленно

Page 28: Павел Довгалюк, Обратная отладка

Обратные отладчики native приложений

Отдельные приложения

GDB

Mozilla RR

TotalView

UndoDB

Виртуальные машины

QEMU

Wind River Simics

Page 29: Павел Довгалюк, Обратная отладка

gdb

Версия 7+

Встроенная поддержка i386-linux, amd64-linux Буфер на 200000 инструкций

set record insn-number-max

Удаленная отладка (Simics, UndoDB и другие)

Page 30: Павел Довгалюк, Обратная отладка

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;

}

Page 31: Павел Довгалюк, Обратная отладка

gdb

Повторные запуски программы могут размещаться по новым адресам

checkpoint – сохраняет снимок программы

restore id – восстанавливает указанный снимок

Каждый снимок представлен отдельным процессом в памяти

Позволяет исследовать разные ветки выполнения

Page 32: Павел Довгалюк, Обратная отладка

gdb

Не отображает вывод на экран/консоль

Поддерживает только x86 под Linux

Длина буфера инструкций ограничена

Медленно работает gzip замедляется в 50000 раз

Page 33: Павел Довгалюк, Обратная отладка

Mozilla RR

Используется для отладки Firefox

Записывает меньше данных, чем GDB

Сохраняются результаты выполнения системных вызовов, переключение потоков и т.п.

Работает относительно быстро

20-40% overhead при записи обычных приложений

В худшем случае ~300%

Page 34: Павел Довгалюк, Обратная отладка

Mozilla RR

Записывает недетерминированные входы clock_gettime(...&now);

read(fd, buf, 4096);

__asm__("rdtsc")

ioctl(...)

UNIX signals...

Во время воспроизведения эмулирует эти вызовы

Не записывает процессы и потоки параллельно

Сериализует многопоточные приложения, чтобы всё записалось корректно

Каждому потоку по очереди выделяется интервал времени для работы

Page 35: Павел Довгалюк, Обратная отладка

Mozilla RR

Автоматически запускает GDB при воспроизведении

Поддерживает команды обратной отладки

reverse-step

reverse-next

reverse-finish

reverse-continue

Поддерживает GDB checkpoint

Page 36: Павел Довгалюк, Обратная отладка

Mozilla RR

По умолчанию сохраняет сценарий в файл

Поддерживает запись многопоточных приложений

Работает не на всех процессорах из-за использования аппаратных счетчиков

Поддержки ARM в ближайшее время не будет

Поддерживаются не все системные вызовы

Не воспроизводит графический интерфейс

Page 37: Павел Довгалюк, Обратная отладка

QEMU

Эмулятор с открытым исходным кодом

Эмулирует x86, ARM, MIPS, PowerPC и др.

Page 38: Павел Довгалюк, Обратная отладка

Отладка драйверов и операционных систем

38

Падение системы

Замедление работы

Изменение поведения из-за отладочных методов

Page 39: Павел Довгалюк, Обратная отладка

Отладка ОС через gdbserver в симуляторе

39

Не нужно сохранять дампы, чтобы их анализировать

Можно подключиться в любой момент

Даже при критическом сбое

Даже до загрузки ОС

Отладка без реального оборудования

Отладка прошивки

Отладка моделей оборудования

Работает медленнее из-за виртуализации

Ход работы может измениться из-за остановок

Page 40: Павел Довгалюк, Обратная отладка

Детерминированное воспроизведение и обратная отладка

40

Должна работать на всех платформах QEMU

Протестирована для x86, x64, ARM, MIPS

Отладка и анализ всей системы

Ядро и BIOS

Виртуальные устройства

Во время воспроизведения можно переключиться на обычную работу

Работает медленнее из-за виртуализации

Page 41: Павел Довгалюк, Обратная отладка

QEMU

41

Опубликованы все патчи для обратной отладки

около 6000 LOC

В QEMU 2.5 включено ядро воспроизведения

Без блочных устройств, gdb, сети, USB

Патчи для дисковых устройств в работе

Page 42: Павел Довгалюк, Обратная отладка

Почему же люди не используют обратную отладку? © Stackexchange, 2013

В GDB появилась в 2009 году

Historical debugging в MSVS

Нужен специальный отладчик

Работает медленно (в GDB)

Люди думают, что она не работает

Page 43: Павел Довгалюк, Обратная отладка

Обратная отладка

Использование обратной отладки на 26% снижает время, затрачиваемое на поиск ошибок

Cambridge’s Judge Business School

Бесплатный отладчик Mozilla RR

Бесплатный отладчик GDB