역 디버깅은 어떻게 작동합니까?


82

GDB에는 역 디버그를 지원하는 새로운 버전이 있습니다 ( http://www.gnu.org/software/gdb/news/reversible.html 참조 ). 어떻게 작동하는지 궁금합니다.

리버스 디버그가 작동하려면 각 단계의 메모리를 포함하여 전체 시스템 상태를 저장해야하는 것 같습니다. 이것은 많은 메모리를 사용하는 것은 말할 것도없이 성능을 엄청나게 느리게 만듭니다. 이러한 문제는 어떻게 해결됩니까?


4
전체 상태가 아닌 상태 델타를 저장하면 얻을 수 있다고 생각하지만 여전히 비용이 많이 드는 것 같습니다.
지출 자


델타 저장은 실제로 매우 잘 작동 할 수 있으며 효율적인 전체 시스템 가역 솔루션에 실제로 필요합니다.
jakobengblom2

답변:


131

저는 gdb 관리자이며 새로운 역 디버깅의 저자 중 한 명입니다. 어떻게 작동하는지 이야기하게되어 기쁩니다. 여러 사람이 추측했듯이 나중에 복원 할 수 있도록 충분한 시스템 상태를 저장해야합니다. 여러 가지 방식이 있으며 그 중 하나는 각 기계 명령어에 의해 수정되는 레지스터 또는 메모리 위치를 단순히 저장하는 것입니다. 그런 다음 해당 명령을 "실행 취소"하려면 해당 레지스터 또는 메모리 위치의 데이터를 되돌립니다.

예, 비용이 많이 들지만 최신 CPU는 너무 빠르기 때문에 어쨌든 상호 작용할 때 (스테핑 또는 중단 점 수행) 실제로 그다지 눈치 채지 못합니다.


4
그러나 리버스 디버깅 을 사용하면 입력 한 명령 next과 롤백 만 가능 step합니까? 아니면 명령을 여러 번 실행 취소 할 수 있습니까? 예를 들어, 명령에 중단 점을 설정하고 그때까지 실행되도록두면 건너 뛴 경우에도 이전 명령으로 롤백 할 수 있습니까?
Nathan Fellman

10
> 그러나 리버스 디버깅을 사용하면 입력 한 다음 단계 명령과 단계 명령 만 롤백 할 수 있거나 명령을 실행 취소 할 수 있습니까? 명령을 실행 취소 할 수 있습니다. 예를 들어 앞으로 나아갈 때 멈춘 지점에서만 멈출 수 있습니다. 새 중단 점을 설정하고 역방향으로 실행할 수 있습니다.> 예를 들어 명령에 중단 점을 설정하고 그때까지 실행하도록두면 건너 뛴 후에도 이전 명령으로 롤백 할 수 있습니까? 예 중단 점에 도달하기 전에 녹음 모드를 켰습니다
Michael Snyder

3
형식이 지정되지 않은 텍스트에 대해 죄송합니다. 무슨 일이 일어 났는지 모르겠습니다.
Michael Snyder

10
리버스 디버깅이 시간을 되돌리고 60 년대 나 70 년대로 돌아 가게 할까 걱정됩니다. 벨 보텀을 입고 다시 머리를 기르고 싶지 않아요.
틴 남자

3
그리고 OS에서 상태를 수정하는 syscall은? 제대로 작동하지 않습니까? 불투명 핸들을 수정하면 어떨까요?
Adrian

12

역 실행을 구현하기 위해 시뮬레이터, 가상 머신 및 하드웨어 레코더를 사용하는 것을 잊지 말아야합니다.

이를 구현하는 또 다른 솔루션은 GreenHills 및 Lauterbach가 하드웨어 기반 디버거에서 수행하는 것과 같이 물리적 하드웨어에서 실행을 추적하는 것입니다. 이 고정 된 각 명령의 동작 추적을 기반으로 각 명령의 효과를 차례로 제거하여 추적의 임의 지점으로 이동할 수 있습니다. 이것은 디버거에서 볼 수있는 상태에 영향을 미치는 모든 것을 추적 할 수 있다고 가정합니다.

또 다른 방법은 VmWare Workstation 6.5 및 Virtutech Simics 3.0 이상에서 사용되며 Visual Studio 2010과 함께 제공되는 것으로 보이는 체크 포인트 + 재실행 방법을 사용하는 것입니다. 여기서는 가상 머신 또는 시뮬레이터를 사용합니다. 시스템 실행에 대한 간접적 인 수준을 얻습니다. 정기적으로 전체 상태를 디스크 나 메모리에 덤프 한 다음 시뮬레이터가 정확히 동일한 프로그램 경로를 결정적으로 재실행 할 수 있다는 점에 의존합니다.

단순화하면 다음과 같이 작동합니다. 시스템 실행에서 T 시간에 있다고 가정합니다. 시간 T-1로 이동하려면 t <T 지점에서 체크 포인트를 선택한 다음 (Tt-1) 사이클을 실행하여 이전에 한 사이클을 종료합니다. 이는 매우 잘 작동하도록 만들 수 있으며 디스크 IO를 수행하고 커널 수준 코드로 구성되며 장치 드라이버 작업을 수행하는 워크로드에도 적용됩니다. 핵심은 모든 프로세서, 장치, 메모리 및 IO와 함께 전체 대상 시스템을 포함하는 시뮬레이터를 갖는 것입니다. 자세한 내용 은 gdb 메일 링리스트gdb 메일 링리스트 에 이어 토론을 참조하십시오. 저는이 접근 방식을 자주 사용하여 특히 장치 드라이버와 초기 OS 부팅에서 까다로운 코드를 디버깅합니다.

또 다른 정보 출처는 체크 포인트에 대한 Virtutech 백서입니다 (전체 공개로 작성).


또한 역 디버깅 기술에 대한보다 자세한 설명 은 jakob.engbloms.se/archives/1547 및 다음 두 개의 블로그 게시물을 참조하십시오 .
jakobengblom2

리버스 스테핑을 구현하는 대신 "세이브 포인트 설정"기능은 어떻습니까? 따라서 디버그하고 어느 시점에서 현재 단계를 "저장 지점"으로 선택할 수 있으며 나중에 해당 저장 지점으로 다시 이동하고 다시 앞으로 이동하여 필요한 경우 변수를 편집 할 수 있습니다. VM의 경우 "스냅 샷", OS의 경우 "복원 지점"과 같은 종류입니다.
Rolf

9

EclipseCon 세션 중에 우리는 또한 Chronon Debugger for Java 로이 를 수행하는 방법을 물었습니다 . 그것은 당신이 실제로 뒤로 물러 설 수는 없지만 역 디버깅처럼 느껴지는 방식으로 기록 된 프로그램 실행을 재생할 수 있습니다 . (주요 차이점은 Chronon 디버거에서 실행중인 프로그램을 변경할 수 없지만 대부분의 다른 Java 디버거에서는 변경할 수 있다는 것입니다.)

올바르게 이해하면 실행중인 프로그램의 바이트 코드를 조작하여 프로그램 내부 상태의 모든 변경 사항이 기록됩니다. 외부 상태는 추가로 기록 할 필요가 없습니다. 어떤 식 으로든 프로그램에 영향을 미치는 경우 해당 외부 상태와 일치하는 내부 변수가 있어야합니다 (따라서 해당 내부 변수로 충분합니다).

그런 다음 재생 시간 동안 기본적으로 기록 된 상태 변경에서 실행중인 프로그램의 모든 상태를 다시 만들 수 있습니다.

흥미롭게도 상태 변화는 처음 볼 때 예상하는 것보다 훨씬 작습니다. 따라서 조건부 "if"문이있는 경우 프로그램이 then- 또는 else- 문을 사용했는지 여부를 기록하는 데 최소한 1 비트가 필요하다고 생각할 것입니다. 대부분의 경우 다른 분기에 반환 값이 포함 된 경우처럼이를 피할 수 있습니다. 그런 다음 반환 값 (어쨌든 필요함) 만 기록 하고 반환 값 자체에서 실행 된 분기에 대한 결정 을 다시 계산 하는 것으로 충분 합니다.


8

이 질문은 오래되었지만 대부분의 답변도 마찬가지입니다. 흥미로운 주제로 남아 있으며 2015 년 답변을 게시하고 있습니다. 내 석사 논문의 1 장과 2 장, 컴퓨터 프로그래밍에서 시각적 사고를 향한 역 디버깅과 라이브 프로그래밍 결합, 역 디버깅 에 대한 역사적 접근 방식 중 일부를 다루고 있습니다 (특히 스냅 샷 (또는 체크 포인트) 및 재생 접근 방식에 중점을 두었습니다). 그것과 전지적 디버깅의 차이점을 설명합니다.

프로그램을 어느 시점까지 앞으로 실행 한 컴퓨터는 우리에게 그것에 대한 정보를 제공 할 수 있어야합니다. 이러한 개선이 가능하며 전지전능 디버거에서 발견됩니다. 일반적으로 리버스 디버거로 분류되지만, 프로그래머가 실제로 실행중인 프로그램에서 시간을 거슬러 올라가는 것을 허용하지 않고 실행 중에 정보를 기록하여 나중에 보거나 쿼리 할 뿐이므로 "기록 로깅"디버거로 더 정확하게 설명 될 수 있습니다. . "Omniscient"는 기록 된 프로그램의 전체 상태 기록을 실행 후 디버거에서 사용할 수 있다는 사실에서 비롯됩니다. 그러면 프로그램을 다시 실행할 필요가 없으며 수동 코드 계측이 필요하지 않습니다.

소프트웨어 기반 전지적 디버깅은 "디버그 시간 기록 재생"이라고 불리는 1969 년 EXDAMS 시스템에서 시작되었습니다. GNU 디버거 인 GDB는 2009 년부터 '프로세스 기록 및 재생'기능을 사용하여 전지적 디버깅을 지원했습니다. TotalView, UndoDB 및 Chronon은 현재 사용 가능한 최고의 전 지능적 디버거로 보이지만 상용 시스템입니다. Java의 경우 TOD는 부분 결정적 재생, 부분 추적 캡처 및 분산 데이터베이스를 사용하여 관련된 많은 양의 정보를 기록 할 수있는 최상의 오픈 소스 대안으로 보입니다.

단순히 기록의 탐색을 허용하는 것이 아니라 실제로 실행 시간에서 뒤로 이동할 수있는 디버거도 존재합니다. 시간 이동, 시간 이동, 양방향 또는 리버스 디버거로 더 정확하게 설명 할 수 있습니다.

최초의 시스템은 1981 년 COPE 프로토 타입이었습니다.


4

mozilla rr는 GDB 역 디버깅에 대한보다 강력한 대안입니다.

https://github.com/mozilla/rr

GDB의 내장 레코드 및 재생에는 심각한 제한이 있습니다. 예를 들어 AVX 명령을 지원하지 않습니다 . gdb 역 디버깅이 "프로세스 레코드가 주소에서 명령 0xf0d를 지원하지 않습니다"와 함께 실패합니다.

rr의 장점 :

  • 현재 훨씬 더 안정적입니다. 나는 여러 복잡한 소프트웨어를 비교적 오랫동안 테스트했습니다.
  • 또한 gdbserver 프로토콜을 사용하는 GDB 인터페이스를 제공하므로이를 대체 할 수 있습니다.
  • 대부분의 프로그램에서 약간의 성능 저하, 측정하지 않고는 직접 알아 차리지 못했습니다.
  • 생성 된 추적은 비 결정적 이벤트가 거의 기록되지 않기 때문에 디스크에서 작습니다. 지금까지 그 크기에 대해 걱정할 필요가 없었습니다.

rr은 스레드 스위치와 같은 모든 단일 비 결정적 이벤트에서 발생한 일을 기록하는 방식으로 프로그램을 먼저 실행하여이를 달성합니다.

그런 다음 두 번째 재생 실행 중에 놀랍도록 작은 추적 파일을 사용하여 원래의 비 결정적 실행에서 발생한 일을 정확하게 재구성하지만 정방향 또는 역방향으로 결정적 방식으로 재구성합니다.

rr은 원래 다음날 야간 테스트에서 나타난 타이밍 버그를 재현하기 위해 Mozilla에서 개발했습니다. 그러나 역 디버깅 측면은 실행 중에 몇 시간 만 발생하는 버그가있는 경우에도 기본이됩니다. 이전 상태로 인해 나중에 실패한 내용을 조사하기 위해 종종 뒤로 물러서 고 싶기 때문입니다.

다음 예는 reverse-next, reverse-stepreverse-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의 가장 심각한 한계는 다음과 같습니다.

UndoDB는 rr의 상업적 대안입니다 : https://undo.io 둘 다 추적 / 재생 기반이지만 기능과 성능면에서 어떻게 비교되는지 잘 모르겠습니다.


내가 ddd로 어떻게 할 수 있는지 알아? 감사합니다
spraff 2019

@spraff 잘 모르겠지만 가능성이 있습니다. 먼저 ddd를 gdbserver에 연결하십시오. 작동하면 rr에서도 작동합니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
그러나 @spraff는 ddd를 사용하지 말고 gdb 대시 보드를 사용하십시오 ;-) stackoverflow.com/questions/10115540/gdb-split-view-with-code/… 이것은 일반 GDB이기 때문에 확실히 작동합니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3

Nathan Fellman은 다음과 같이 썼습니다.

그러나 역 디버깅을 사용하면 입력 한 다음 및 단계 명령 만 롤백 할 수 있습니까? 아니면 여러 명령을 실행 취소 할 수 있습니까?

여러 지침을 실행 취소 할 수 있습니다. 예를 들어, 전진 할 때 멈춘 지점에서만 멈춰야하는 것은 아닙니다. 새 중단 점을 설정하고 역방향으로 실행할 수 있습니다.

예를 들어, 명령에 중단 점을 설정하고 그때까지 실행되도록두면 건너 뛴 경우에도 이전 명령으로 롤백 할 수 있습니까?

예. 중단 점에 도달하기 전에 기록 모드를 켜면됩니다.


2
역방향 솔루션의 중요한 부분은 어느 시점에서 켜고 그 시점까지만 되돌릴 수 있다는 것입니다. 기계를 완전히 반대로 돌리고 무슨 일이 일어 났는지에 대한 기록없이 이전에 무슨 일이 있었는지 알아낼 수있는 마법은 없습니다.
jakobengblom2

2

ODB라는 또 다른 역 디버거가 작동하는 방법은 다음과 같습니다 . 추출물:

Omniscient Debugging은 프로그램의 각 "관심 지점"(값 설정, 메서드 호출, 예외 발생 / 잡기)에서 "타임 스탬프"를 수집 한 다음 프로그래머가 해당 타임 스탬프를 사용하여 그 프로그램 실행의 역사.

ODB ...는로드 될 때 프로그램의 클래스에 코드를 삽입하고 프로그램이 실행되면 이벤트가 기록됩니다.

나는 gdb가 같은 종류의 방식으로 작동한다고 생각합니다.


그렇다면 컴파일러와 디버거에게 흥미로운 점이 어디에 있는지 알려주는 코드의 지시문이 필요합니까?
Nathan Fellman

아니요. 작동 방식을 보여주는 Java Web Start 데모가 www.LambdaCS.com/debugger/debugger.html에 있습니다. 일반 프로그램처럼 보입니다. 어쨌든 그것은 ODB입니다. gdb에 대해서는 모릅니다. 그래도 매우 멋지다 :)
demoncodemonkey

gdb 솔루션은 어떤 식 으로든 대상 프로그램을 변경하지 않습니다. 디버깅을 위해 프로그램을 계측해야하는 경우 타이밍 차이 및 기타 장애로 인해 문제가 사라질 가능성이 있습니다. 모든 상용 revexec 도구는 프로그램 자체의 코드를 변경하지 않는 외부 레코드 형식을 기반으로합니다.
jakobengblom2

@ jakobengblom2 : 메모리에 쓰거나 실행을 에뮬레이트하거나 단순히 하드웨어 중단 점을 추가하여 대상을 변경하는 것 사이의 차이에 너무 강조하고 있다고 생각합니다. 그들은 모두 타이밍을 바꿉니다. 실제로 대상 계측은 타이밍을 가장 적게 변경합니다.
Ben Voigt 2013 년

2

역 디버깅은 프로그램을 역방향으로 실행할 수 있음을 의미하며, 이는 문제의 원인을 추적하는 데 매우 유용합니다.

각 단계에 대한 전체 머신 상태를 저장할 필요는 없으며 변경 사항 만 저장할 수 있습니다. 여전히 꽤 비싸다.


알겠습니다.하지만 변경 사항을 저장하려면 각 변경 사항에서 실행을 중단해야합니다.
Nathan Fellman

예, 맞습니다.하지만 기계는 지금 ​​상당히 빠르며 인간의 관점에서 저는 속도 저하가 참을 수 없다고 생각합니다. valgrind와 비슷하지만 valgrind만큼 느리지 않을 수도 있습니다.
Michael Snyder
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.