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

Post on 16-Apr-2017

433 Views

Category:

Software

1 Downloads

Preview:

Click to see full reader

Transcript

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

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

Отладка

Отладка

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

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

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

top related