GDB Server를 사용한 원격 디버깅
이번 포스트에서는 디버거 도구인 gdbserver와 gdb를 사용하여 원격 디버깅하는 방법을 다룹니다.
원격 디버깅은 실제로 애플리케이션이 실행되는 타겟에서 gdbserver를 실행하고, 원격 호스트에서 gdb와 툴체인을 사용하여 디버깅하는 방법입니다.
참조. Linux Foundation Conference 2020
이 방법은 타겟에서 최적화 및 디버깅 심볼이 제거된 애플리케이션을 gdbserver 리스닝을 통해 실행하고, 원격 호스트에서는 툴체인을 통해 최적화 및 디버깅 심볼이 포함된 애플리케이션과 함께 gdb를 사용하여 gdbserver에 연결하여 디버깅하는 방식입니다.
타겟이 임베디드 보드인 경우 디스크 메모리 용량과 시스템 메모리 용량이 매우 제한적이므로, 이러한 경우에 원격 디버깅이 유용합니다.
예제로 살펴보기
- 자세한 설명에 앞서, 기본적으로 커맨드 라인에서 gdb를 어느 정도 사용할 줄 안다고 가정하겠습니다.
0. 환경 설정

위 다이어그램은 두 개의 VS Code 인스턴스가 각각 Docker 컨테이너를 실행하고 있는 상황을 보여줍니다.
- Host IP: 172.25.125.2
- Target IP: 172.25.125.3
1. Hello World 출력하기
테스트하려는 시나리오는 다음과 같습니다:
- 호스트에서 타겟용으로 Helloworld.cpp를 컴파일하여 타겟으로 전송
- Not Stripped, 디버그 정보 포함, 최적화 없음
- GDBServer와 GDB 연결
- 네트워크를 통해 호스트에서 타겟의 애플리케이션을 실시간 디버깅
Helloworld.cpp 테스트 코드는 다음과 같습니다:
#include <iostream>
#include <vector>
int main(){
std::vector<int> intVector {1,2,3,4,5,6,7};
for (auto i : intVector){
std::cout << "Hello World : "<< i << std::endl;
}
return 0;
}
1. 호스트에서 타겟용으로 Helloworld.cpp를 컴파일하여 타겟으로 전송

2. GDBServer와 GDB 연결
-
[TARGET] 먼저 타겟에서 GDBServer를 시작하여 리스닝 상태로 만들어봅시다

-
[HOST] GDB를 실행하고 네트워크를 통해 GDBServer에 연결

-
[HOST, TARGET] 성공적으로 연결된 화면

4. 네트워크를 통해 호스트에서 타겟의 애플리케이션을 실시간 디버깅
-
[HOST] main 함수에 브레이크포인트를 설정하고 확인

-
[HOST] 실행

- 원격으로 연결된 gdb에서는 run 명령을 사용할 수 없습니다
- HOST에서 ‘n’으로 한 줄씩 진행하면 TARGET의 stdout에 std::cout의 메시지가 나타나는 것을 볼 수 있습니다
2. 실행 중인 애플리케이션에 어태치하기
GDBServer를 사용하여 처음부터 애플리케이션을 시작하는 것 외에도, 이미 실행 중인 애플리케이션에 실시간으로 어태치할 수도 있습니다.
아래와 같이 무한 루프로 실행되는 예제를 사용하여 설명하겠습니다:
//running_app.cpp
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
int main(){
using namespace std::chrono_literals;
while (true){
std::vector<int> intVector {1,2,3,4,5,6,7};
for (auto i : intVector){
std::cout << "Hello World : "<< i << std::endl;
std::this_thread::sleep_for(1s);
}
}
return 0;
}
위 예제는 매초마다 “Hello World : 1 ~ 7”을 stdout에 반복적으로 출력합니다.
[TARGET] 먼저 위에서 빌드한 애플리케이션을 타겟으로 이동하여 실행해봅시다.
-
running_app이 pid=25685로 할당되어 실행 중입니다.

[TARGET] running_app에 gdbserver를 어태치해봅시다
-
정상적으로 리스닝이 시작되면 아래와 같은 로그가 표시됩니다

[HOST] gdb로 TARGET에 다음과 같이 연결합니다

-
연결하자마자 TARGET의 동작이 일시 정지됩니다

-
로그를 보니 연결 시점은 다음을 실행하고 있을 때였습니다

std::this_thread::sleep_for(1s);- nanosleep.c 쪽의 소스 코드가 없어서 심볼 추적이 불가능합니다
[HOST] n이나 s 명령을 사용하여 라인별 디버깅을 진행할 수 있습니다
-
n 명령 (next)

-
s 명령 (step into)

[HOST] detach를 통해 정상 동작을 재개할 수 있습니다
-
마지막으로 디버깅하던 지점부터 정상 동작을 재개합니다
