Programing

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

lottogame 2020. 10. 10. 09:28
반응형

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


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

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


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

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


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

이를 구현하는 또 다른 솔루션은 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 백서입니다 (전체 공개로 작성했습니다).


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

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

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

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


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

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

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

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

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


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

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

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

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

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


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

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

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

gdb가 같은 방식으로 작동한다고 생각합니다.


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

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


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

https://github.com/mozilla/rr

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

rr의 장점 :

  • 현재 훨씬 더 안정적
  • 또한 gdbserver 프로토콜을 사용하는 GDB 인터페이스를 제공하므로이를 대체 할 수 있습니다.
  • 많은 프로그램에 대한 작은 성능 저하

다음 예는 reverse-next, reverse-stepreverse-continue명령 같은 일부 기능을 보여줍니다 .

Ubuntu 16.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;

참고 URL : https://stackoverflow.com/questions/1470434/how-does-reverse-debugging-work

반응형