Як працює зворотне налагодження?


82

GDB має нову версію, яка підтримує зворотну налагодження (див. Http://www.gnu.org/software/gdb/news/reversible.html ). Мені цікаво, як це працює.

Мені здається, що для роботи зворотної налагодження потрібно зберігати весь стан машини, включаючи пам’ять для кожного кроку. Це зробило б продуктивність неймовірно повільною, не кажучи вже про використання великої кількості пам'яті. Як вирішуються ці проблеми?


4
Думаю, ви могли б обійтися зберіганням державних дельт, а не всього штату, але все ще здається, що це може коштувати дорого.
Спендер


Збереження дельт дійсно може працювати дуже добре, і це дійсно необхідно для ефективного повноцінного оборотного рішення.
jakobengblom2

Відповіді:


131

Я підтримуючий gdb і один з авторів нової зворотної налагодження. Я був би радий розповісти про те, як це працює. Як припускали кілька людей, вам потрібно зберегти достатньо стану машини, щоб ви могли відновити її пізніше. Існує ряд схем, одна з яких полягає у простому збереженні регістрів або місць пам'яті, які змінені кожною машинною інструкцією. Потім, щоб "скасувати" цю інструкцію, ви просто повертаєте дані в ці регістри або місця пам'яті.

Так, це дорого, але сучасні процесори настільки швидкі, що коли ви інтерактивні в будь-якому випадку (робите кроки або точки зупинки), ви насправді цього не так сильно помічаєте.


4
Але чи дозволяє зворотне налагодження дозволяти лише відкат nextі stepкоманди, які ви ввели, або скасовує будь-яку кількість інструкцій? Наприклад, якщо я встановив точку зупинки в інструкції і дозволив їй працювати до тих пір, чи можу я тоді повернутися до попередньої інструкції, хоча я її пропустив?
Nathan Fellman

10
> Але чи дозволяє зворотне налагодження лише відкочувати наступні та крокові команди, які ви ввели, або дозволяє скасувати будь-яку кількість інструкцій Ви можете скасувати будь-яку кількість інструкцій. Ви не обмежені, наприклад, зупиняйтеся лише на тих місцях, де ви зупинилися, коли йшли вперед. Ви можете встановити нову точку зупинки та запустити її назад> Наприклад, якщо я встановив точку зупинки в інструкції і дозволив їй працювати до тих пір, чи можу я потім повернутися до попередньої інструкції, хоча я пропустив її так Так довго, поки ви увімкнув режим запису, перш ніж ви побігли до точки зупинку
Майкл Снайдер,

3
Вибачте за неформатований текст, не знаю, що з цим.
Майкл Снайдер

10
Я переживаю, що зворотне налагодження може скасувати час і змусити нас повернутися до 60-х чи 70-х років. Я не хочу носити дзвіночки і знову відрощувати своє волосся.
Олов'яний Чоловік

3
А сискали, які змінюють стан в ОС? Це просто не працює належним чином? Як щодо того, коли він змінює непрозорий дескриптор?
Адріан

12

Зверніть увагу, що ви не повинні забувати про використання симуляторів, віртуальних машин та апаратних реєстраторів для реалізації зворотного виконання.

Іншим рішенням для його реалізації є відстеження виконання на фізичному обладнанні, як це робиться GreenHills та Lauterbach в їх апаратних налагоджувачах. На основі цього фіксованого сліду дії кожної інструкції ви можете перейти до будь-якої точки трасування, видаляючи по черзі ефекти кожної інструкції. Зверніть увагу, що це передбачає, що ви можете простежити всі речі, які впливають на стан, видимий у налагоджувачі.

Іншим способом є використання контрольної точки + метод повторного виконання, який використовується VmWare Workstation 6.5 та Virtutech Simics 3.0 (і пізніших версій), і який, здається, поставляється з Visual Studio 2010. Тут ви використовуєте віртуальну машину або симулятор щоб отримати рівень непрямого впливу на виконання системи. Ви регулярно скидаєте весь стан на диск або пам'ять, а потім покладаєтесь на можливість симулятора детерміновано повторно виконати той самий шлях програми.

Спрощено, це працює так: скажіть, ви знаходитесь у момент часу Т у виконанні системи. Щоб перейти до часу T-1, ви берете якусь контрольну точку з точки t <T, а потім виконуєте цикли (Tt-1), щоб закінчити один цикл до того, де ви були. Це може змусити працювати дуже добре і застосовуватись навіть для робочих навантажень, які виконують введення-виведення диска, складаються з коду рівня ядра та виконують роботу драйвера пристрою. Головне - мати симулятор, який містить всю цільову систему з усіма її процесорами, пристроями, пам’яттю та введенням-виводом. Детальніше див. У списку розсилки gdb та обговоренні, що йдуть за ним у списку розсилки gdb. Я сам використовую цей підхід досить регулярно для налагодження хитрого коду, особливо в драйверах пристроїв та ранніх завантаженнях ОС.

Іншим джерелом інформації є довідковий матеріал Virtutech про контрольно-пропускний пункт (який я написав, повністю розкривши ).


Також дивіться jakob.engbloms.se/archives/1547 та два наступні повідомлення в блозі, щоб отримати більш ретельне проходження методів зворотної налагодження.
jakobengblom2

Як щодо можливості "встановлення очок збереження" замість здійснення зворотного кроку. Отже, ви налагоджуєте, і в якийсь момент ви можете вибрати поточний крок як "точку збереження", а пізніше у вас буде можливість перейти назад до цієї точки збереження і зробити крок вперед, редагуючи свої змінні, якщо це необхідно. Начебто "знімки" для віртуальних машин або "точки відновлення" для ОС.
Рольф

9

Під час сеансу EclipseCon ми також запитали, як вони роблять це за допомогою налагоджувача Chronon для Java. Цей не дозволяє вам фактично відступити, але може відтворити записане виконання програми таким чином, що це здається зворотним налагодженням. (Головна відмінність полягає в тому, що ви не можете змінити запущену програму в налагоджувачі Chronon, тоді як це можна зробити в більшості інших налагоджувачів Java.)

Якщо я це правильно зрозумів, він маніпулює байт-кодом запущеної програми таким чином, що реєструється кожна зміна внутрішнього стану програми. Зовнішні держави не потрібно додатково реєструвати. Якщо вони якимось чином впливають на вашу програму, тоді у вас повинна бути внутрішня змінна, яка відповідає цьому зовнішньому стану (а отже, цієї внутрішньої змінної достатньо).

Під час відтворення вони можуть в основному відтворити кожен стан запущеної програми із записаних змін стану.

Цікаво, що зміни в державі значно менші, ніж можна було б очікувати на перший погляд. Отже, якщо у вас є умовне твердження "якщо", ви могли б подумати, що вам потрібен принаймні один біт, щоб записати, чи програма використовувала оператор then - або else -. У багатьох випадках ви можете уникнути навіть цього, як у випадку, коли ці різні гілки містять повернене значення. Тоді достатньо записати лише повернене значення (яке в будь-якому випадку знадобиться) і перерахувати рішення про виконану гілку з самого поверненого значення.


8

Хоча це питання давнє, більшість відповідей теж є, і як залишається цікавою темою, я публікую відповідь 2015 року. Розділи 1 і 2 моєї дисертації, що поєднує зворотну налагодження та програмування в реальному часі в напрямку візуального мислення в комп'ютерному програмуванні , охоплює деякі історичні підходи до зворотної налагодження (особливо зосереджених на підході до моментального знімка (або контрольної точки) та відтворення), а також пояснює різницю між нею та всезнаючою налагодженням:

Комп’ютер, виконавши програму до певного моменту, справді повинен мати можливість надати нам інформацію про неї. Таке вдосконалення можливо, і воно виявляється в тому, що називають всезнаючими налагоджувачами. Зазвичай вони класифікуються як зворотні налагоджувачі, хоча їх можна більш точно охарактеризувати як налагоджувачі "журналу історії", оскільки вони просто записують інформацію під час виконання для перегляду або запиту пізніше, а не дозволяють програмісту фактично зробити крок назад у часі у виконуючій програмі. . "Всезнаючий" походить від того, що вся історія стану програми, яка була записана, доступна налагоджувачеві після виконання. Тоді немає необхідності повторно запускати програму, а також немає необхідності в ручному вимірюванні коду.

Всезнаюча налагодження, заснована на програмному забезпеченні, розпочалася з системи EXDAMS 1969 року, де її називали "відтворення історії часу налагодження". Налагоджувач GNU, GDB, підтримує всезнаючу налагодження з 2009 року, з її функцією `` запис процесу та відтворення ''. TotalView, UndoDB та Chronon, здається, є найкращими всезнаючими налагоджувачами, які зараз доступні, але є комерційними системами. TOD, для Java, виявляється найкращою альтернативою з відкритим кодом, яка використовує часткове детерміноване відтворення, а також часткове захоплення трасування та розподілену базу даних, щоб забезпечити запис великих обсягів задіяної інформації.

Існують також налагоджувачі, які не просто дозволяють навігацію записом, але насправді можуть зробити крок назад під час виконання. Їх можна більш точно описати як налагоджувачі часу, подорожі в часі, двонаправлені або зворотні налагоджувачі.

Першою такою системою став прототип COPE 1981 року ...


4

mozilla rrє більш надійною альтернативою зворотній налагодженні GDB

https://github.com/mozilla/rr

Вбудований запис та повторне відтворення GDB має серйозні обмеження, наприклад, відсутність підтримки інструкцій AVX: зворотне налагодження gdb не вдається з "Запис процесу не підтримує інструкцію 0xf0d за адресою"

Недоліки rr:

  • в даний час набагато надійніше. Я протестував його відносно тривалий цикл декількох складних програм.
  • також пропонує інтерфейс GDB з протоколом gdbserver, що робить його чудовою заміною
  • невелике падіння продуктивності для більшості програм, я сам цього не помітив, не роблячи вимірювань
  • згенеровані сліди невеликі на диску, оскільки записано дуже мало недетермінованих подій, мені ніколи не доводилося турбуватися про їх розмір

rr досягає цього, спочатку запускаючи програму таким чином, щоб фіксувати те, що відбувалося з кожною окремою недетермінованою подією, такою як перемикач потоків.

Потім під час другого запуску повторного використання він використовує той файл трасування, який напрочуд малий, щоб реконструювати саме те, що сталося в початковому недетермінованому запуску, але детермінованим способом, вперед чи назад.

Спочатку rr був розроблений Mozilla, щоб допомогти їм відтворити помилки синхронізації, які виявилися під час щовечірнього тестування наступного дня. Але зворотний аспект налагодження також є фундаментальним для того, коли у вас є помилка, яка трапляється лише години в процесі виконання, оскільки ви часто хочете зробити крок назад, щоб перевірити, який попередній стан призвів до пізнішої помилки.

Наступний приклад демонструє деякі з його особливостей, в зокрема reverse-next, reverse-stepі reverse-continueкоманду.

Встановити на Ubuntu 18.04:

sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
# Overcome "rr needs /proc/sys/kernel/perf_event_paranoid <= 1, but it is 3."
echo 'kernel.perf_event_paranoid=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Програма випробувань:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int f() {
    int i;
    i = 0;
    i = 1;
    i = 2;
    return i;
}

int main(void) {
    int i;

    i = 0;
    i = 1;
    i = 2;

    /* Local call. */
    f();

    printf("i = %d\n", i);

    /* Is randomness completely removed?
     * Recently fixed: https://github.com/mozilla/rr/issues/2088 */
    i = time(NULL);
    printf("time(NULL) = %d\n", i);

    return EXIT_SUCCESS;
}

компілюємо та запускаємо:

gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay

Тепер ви залишились у сеансі GDB, і ви можете правильно змінити налагодження:

(rr) break main
Breakpoint 1 at 0x55da250e96b0: file a.c, line 16.
(rr) continue
Continuing.

Breakpoint 1, main () at a.c:16
16          i = 0;
(rr) next
17          i = 1;
(rr) print i
$1 = 0
(rr) next
18          i = 2;
(rr) print i
$2 = 1
(rr) reverse-next
17          i = 1;
(rr) print i
$3 = 0
(rr) next
18          i = 2;
(rr) print i
$4 = 1
(rr) next
21          f();
(rr) step
f () at a.c:7
7           i = 0;
(rr) reverse-step
main () at a.c:21
21          f();
(rr) next
23          printf("i = %d\n", i);
(rr) next
i = 2
27          i = time(NULL);
(rr) reverse-next
23          printf("i = %d\n", i);
(rr) next
i = 2
27          i = time(NULL);
(rr) next
28          printf("time(NULL) = %d\n", i);
(rr) print i
$5 = 1509245372
(rr) reverse-next
27          i = time(NULL);
(rr) next
28          printf("time(NULL) = %d\n", i);
(rr) print i
$6 = 1509245372
(rr) reverse-continue
Continuing.

Breakpoint 1, main () at a.c:16
16          i = 0;

Під час налагодження складного програмного забезпечення ви, швидше за все, добігнете до точки збою, а потім потрапите в глибокий кадр. У цьому випадку не забувайте, що для reverse-nextвищих кадрів спочатку потрібно:

reverse-finish

до цього кадру upнедостатньо просто зробити звичайне .

На мою думку, найсерйознішими обмеженнями rr є:

  • https://github.com/mozilla/rr/issues/2089 вам доведеться зробити повторне відтворення з нуля, що може коштувати дорого, якщо збій, який ви намагаєтесь налагодити, трапляється, скажімо, за кілька годин до виконання
  • Лише https://github.com/mozilla/rr/issues/1373 x86

UndoDB є комерційною альтернативою rr: https://undo.io Обидва базуються на відстеженні / відтворенні, але я не впевнений, як вони порівнюються з точки зору функцій та продуктивності.


Чи знаєте ви, як я можу це зробити за допомогою ddd? Спасибі
спрафф

@spraff Я не впевнений, але, ймовірно. Спочатку спробуйте підключити ddd до gdbserver. Якщо це працює, це також має працювати з rr.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
@spraff, однак, не використовуйте ddd, використовуйте інформаційну панель gdb ;-) stackoverflow.com/questions/10115540/gdb-split-view-with-code/... Це точно буде працювати, оскільки це просто звичайний GDB.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3

Натан Феллман писав:

Але чи дозволяє зворотне налагодження лише відкочувати наступні та крокові команди, які ви ввели, або дозволяє скасувати будь-яку кількість інструкцій?

Ви можете скасувати будь-яку кількість інструкцій. Ви не обмежуєтесь, наприклад, зупинкою лише в тих місцях, де зупинилися, коли йшли вперед. Ви можете встановити нову точку зупинки та запустити назад до неї.

Наприклад, якщо я встановив точку зупинки в інструкції і дозволив їй працювати до тих пір, чи можу я тоді повернутися до попередньої інструкції, хоча я її пропустив?

Так. До тих пір, поки ви ввімкнули режим запису, перш ніж бігти до точки зупинки.


2
Найважливішою частиною будь-якого зворотного рішення є те, що ви вмикаєте його в певний момент і можете повертати назад лише до цього моменту. Немає такої магії, яка могла б запустити машину в справжньому зворотному напрямку і дізнатися, що сталося раніше, без певного запису про те, що сталося.
jakobengblom2

2

Ось як працює ще один зворотний налагоджувач, який називається ODB. Екстракт:

Всезнаюча налагодження - це ідея збору "позначок часу" в кожній "точці інтересу" (встановлення значення, здійснення виклику методу, викидання / ловлення винятку) у програмі, а потім дозволяє програмісту використовувати ці мітки часу для дослідження історія запуску цієї програми.

ODB ... вставляє код у класи програми під час їх завантаження, а коли програма запускається, події записуються.

Я припускаю, що gdb працює так само.


То чи для цього потрібні директиви в коді, щоб повідомити компілятору та налагоджувачу, де ці цікаві моменти?
Натан Феллман,

Ні. На веб-сайті www.LambdaCS.com/debugger/debugger.html є демонстрація Java Web Start, яка показує, як це працює. Це схоже на звичайну програму. Це все одно ODB, не знаю про gdb. Хоча це дуже класно :)
demoncodemonkey

Зауважте, що рішення gdb НЕ змінює цільову програму жодним чином. Якщо вам доводиться інструментувати програму для її налагодження, ви маєте шанс зникнути проблему через різницю в термінах та інші порушення. Усі комерційні засоби revexec базуються на певній формі зовнішнього запису, який не змінює код самої програми.
jakobengblom2

@ jakobengblom2: Я думаю, ви занадто акцентуєте увагу на різницю між зміною цілі шляхом запису в її пам'ять, емуляції виконання або просто додавання апаратних точок зупинку. Усі вони змінюють терміни. Насправді цільові прилади, ймовірно, найменше змінюють терміни.
Ben Voigt

2

Зворотне налагодження означає, що ви можете запустити програму назад, що дуже корисно для виявлення причини проблеми.

Вам не потрібно зберігати повний стан машини для кожного кроку, а лише зміни. Це, мабуть, все ще досить дорого.


Я розумію, але вам все одно потрібно порушувати виконання при кожній зміні, щоб зберегти зміни.
Натан Феллман

Так, це правильно, але машини зараз досить швидкі, і по-людськи я не вважаю, що уповільнення неможливо. Це можна порівняти з валгриндом, можливо, не таким повільним, як валгринд.
Michael Snyder
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.