GDB를 활용한 Ruby 디버깅은 다음과 같은 주요 단계를 통해 이루어집니다.
GDB 기본 사용법
-
C 프로그램 컴파일 시
-ggdb옵션을 사용하여 디버깅 심볼을 포함합니다. -
break명령으로 중단점을 설정하고,run으로 프로그램을 실행합니다. -
where명령으로 네이티브 백트레이스를 확인하고,call및p명령으로 변수 값을 출력하거나 함수를 직접 호출할 수 있습니다.
Ruby 백트레이스 획득
-
실행 중인 Ruby 프로세스에 GDB를 연결한 후
call rb_backtrace()를 호출하여 Ruby 백트레이스를 얻을 수 있습니다. -
서비스로 실행되어
stderr에 접근할 수 없는 경우,dup,creat,dup2,close시스템 호출을 GDB 내에서 사용하여 임시 파일로stderr을 리디렉션하여 백트레이스를 추출할 수 있습니다.
다중 스레드 디버깅
-
info threads명령으로 모든 스레드를 나열하고,thread <ID>로 특정 스레드로 전환할 수 있습니다. -
모든 스레드의 Ruby 백트레이스를 자동으로 추출하기 위해 GDB 스크립트를 활용합니다.
rb_thread_list()로 모든 Ruby 스레드를 가져오고, 각 스레드에 대해rb_thread_backtrace_m()을 호출하여 백트레이스를 얻습니다. 이 과정에서stdout을 임시 파일로 리디렉션하여 결과를 저장합니다.
Ruby VM 내부 탐색
-
ruby_current_ec전역 변수를 통해 현재execution_context에 접근할 수 있습니다.execution_context는 호출 스택, 제어 프레임 포인터(cfp), 스레드 포인터 등 Ruby VM의 핵심 정보를 담고 있습니다. -
cfp는 현재 제어 프레임을 가리키며, 이를 통해 프로그램 스택과 YARV(Ruby의 내부 바이트코드 인터프리터) 스택에 접근합니다. -
iseq(instruction sequence) 구조체 내의location필드를 통해 코드의 시작 및 끝 라인 정보를 얻을 수 있으며,rb_vm_get_sourceline(cfp)함수를 호출하여 현재 실행 중인 코드의 정확한 라인 넘버를 파악합니다. -
iseq->body->location.pathobj를 파싱하여 소스 파일명을 얻을 수 있습니다. 이때 Ruby의 내부RString및RArray구조체 표현 방식을 이해해야 합니다. 이들은 작은 문자열/배열을 직접 구조체에 임베드하거나(embed/ary), 큰 값을 위해 포인터(ptr)를 사용하는 최적화가 적용됩니다.