졸업 논문(임베디드 소프트웨어 결함 분석을 위한 효율적인 Record and Replay 기법 개발)을 작성하기 위한 전체적인 레이아웃을 나타낸다.
Abstract
Introduction
배경(동향)
소프트웨어 프로젝트에서의 성패를 결정짓는 과정 중 하나가 디버깅이라고 할 만큼, 예전에 비해서 디버깅의 중요성은 점점 강조되어지고 있다.
프로그램 수행 도중에 버그가 발생했을 때, 서버 또는 데스크 탑 기반의 소프트웨어와는 달리 임베디드 소프트웨어는 제한된 환경에서 디버깅을 해야 하기 때문에 더 많은 시간과 비용이 요구된다.
또한 임베디드 장비들이 소형화되고, 여러가지 기능들이 요구되어짐에 따라 임베디드 시스템 위에서 동작하는 임베디드 소프트웨어는 점점 복잡해지고 있다.
동기(문제점)
효과적인 디버깅을 하기 위해서는 버그가 발생했을 당시의 상황을 그대로 재현하여 버그의 원인을 찾아내야 한다.
coredump 를 이용하는 디버깅 방법의 경우, coredump 가 생성 되었던 당시의 정보만을 바탕으로 재현할 수는 있지만, 이 경우 coredump 가 생성되기 전에 정보를 얻을 수 없기 때문에 버그의 원인이 되는 단서를 탐지하는데 한계가 있다.
또한 기존의 record & replay 디버깅 방법의 경우, 프로그램의 실행 정보를 얻기 위해 소스코드에 실행 정보를 record 하는 코드를 삽입함으로서 실행 결과의 변화를 초래하는 probe 효과를 발생시킨다.
대부분의 임베디드 소프트웨어 디버깅의 경우 타겟(on-target) 자체의 디버깅 보다는 UART, JTAG 과 같은 인터페이스를 이용한 호스트에서의 원격 디버깅 방법을 사용한다. 이러한 방법은 호스트와 타겟 간의 시간 차로 인한 지연으로 버그가 발생한 당시의 상황을 재현하기 힘들다.
요즘 들어, 이러한 문제점을 보완하기 위해 고성능의 H/W 기반의 디버깅 장비나 S/W 디버깅 도구들이 출시되고 있다.
하지만, 가격이 고가이면서도 개발환경(컴파일러, 아키텍처, RTOS)에 의존적이기 때문에 디버깅에 제한이 있다.
목적
이러한 문제점을 해결하기 위해서 본 논문에서는 Record & Replay 기법을 이용한 효율적인 임베디드 소프트웨어 디버깅 방법을 제시한다.
- 소프트웨어가 수행 도중, 이벤트(h/w interrupt, s/w interrupt, system call) 발생시, 로그(log) 데이터를 저장함으로서, 나중에 이벤트가 발생했던 상태를 replay 할 수 있다.
- kernel 에 built-in 됨으로서, 타겟(on-target) 디버깅이 가능하고, replay 시에 디버깅 명령어를 이용하여, 버그의 원인을 탐지할 수 있다.
- 커널 레벨에서 record & replay 기법을 사용함으로서, 커널에 대한 지식이 없는 개발자나 사용자들도 손쉽게 kernel-aware 를 통한 추상화된 커널 정보를 얻을 수 있다.
- vpos 뿐만 아니라 uc/os 와 같은 범용 RTOS 에도 여기서 제시한 record & replay 기법을 사용하여 효율적인 디버깅이 가능하다.
해결방법
임베디드 시스템 상에서 프로그램이 수행될 때 발생하는 이벤트(h/w interrupt, s/w interrupt, system call) 마다, 이벤트 로그(log) 데이터를 메모리의 특정 영역에 저장한다.
이후, 실행 도중 익셉션이 발생하거나, 프로그램 수행이 종료되면, 메모리의 특정 영역에 저장되어 있는 로그(log) 데이터는 플래시 메모리에 저장된다.
타겟 재부팅 후에, replay 모드에서 플래시 메모리에 저장된 이벤트 로그(log) 데이터들이 발생한 순서대로 리스트화 되어 출력되고, 여기서 replay 하고자 하는 이벤트를 선택한다.
선택한 이벤트가 발생한 시점에 PC 레지스터가 가리키고 있던 메모리 주소에 저장되어 있는 명령어 코드는 메모리의 다른 영역에 백업(backup)되고, 여기에 트랩(trap)을 발생시키는 코드를 삽입한다.
프로그램이 수행되면서, 삽입된 코드에 의해 트랩이 발생하게 되면, 디버깅 명령어를 사용할 수 있는 디버깅 모드로 진입하게 된다. 명령어를 통해 선택한 이벤트(h/w interrupt, s/w interrupt, system call)가 발생할 당시의 여러가지 정보들을 알아봄으로서, 버그의 원인을 탐지할 수 있다.
관련 연구
초기의 record & replay 관련 연구들은 프로그램 수행 중에 발생하는 이벤트 정보와 그에 따른 순서와 내용을 모두 저장하는 방식을 사용했다. 이 방법의 경우, 비교적 정확한 replay 보장할 수 있다는 장점이 있는 반면, 저장되는 정보의 양이 비효율적으로 커진다는 단점이 있다.
그 이후, 프로그램이 수행 중에 공유 메모리에 접근하는 순서만을 기록하는 방법이나 메시지나 세마포어를 이용한 동기화 이벤트 만을 저장하는 방법이 연구되었다.
하지만, 프로그램을 정확히 replay 하기 위해 부족하거나, probe 효과가 비교적 커짐에 따른 실제 프로그램 수행과 다른 결과를 초래하였다.
최근에는 s/w 적인 접근 뿐만이 아닌, h/w 접근을 통해 좀더 정확하고, 효율적인 record & replay 방법이 연구되고 있다.
본 논문에서는 POSIX API 를 지원하는 vpos 에 record & replay 기법을 적용하여 임베디드 시스템 환경에서 애플리케이션의 결함을 탐지하도록 구현하였다.
Overview
개발자가 작성한 프로그램은 vpos 의 쉘(shell) 쓰레드에 의해서 명령어를 입력받아 수행된다. 수행 도중 발생하는 이벤트(h/w interrupt, s/w interrupt, system call)에 의해서 스케줄러가 실행되고 context switch 가 일어난다.
이때, 이벤트 로그(log) 데이터를 메모리의 특정 영역에 저장한다.
프로그램 수행 도중, 익셉션이 발생하거나, 프로그램 수행이 종료되면, 메모리의 특정 영역에 저장했던 이벤트 로그(log) 데이터를 플래시 메모리에 저장한다.
타겟(target) 재부팅 후, replay 모드에서는 앞서 플래시 메모리에 저장했던 이벤트 로그(log) 데이터를 메모리로 복사하여, 발생한 순서대로 이벤트 로그(log) 리스트를 출력한다.
replay 하고자 하는 이벤트 로그(log) 데이터를 리스트에서 선택하면, 이벤트(s/w interrupt, h/w interrupt)가 발생했던 곳의 명령어 코드를 메모리의 다른 영역으로 백업해두고, 트랩(trap) 을 발생시키는 명령어 코드를 삽입한다. 프로그램을 실행한다. 프로그램 수행 도중에 트랩(trap)이 발생하면, 디버깅 명령어를 사용할 수 있는 디버깅 모드로 진입하게 된다.
디버깅 관련 명령어를 실행함으로서, 이벤트(s/w interrupt, h/w interrupt) 또는 익셉션이 발생했을 당시의 상태 정보를 출력함으로서 결함의 원인을 탐지할 수 있다.
디버깅 모드에서 지원하는 명령어들은 아래의 표와 같다.
'go' 명령어를 실행하면, 앞에서 트랩(trap) 명령어를 삽입했던 메모리 주소에 코드에 원래 저장되어 있던 명령어 코드를 다시 삽입하고 프로그램 수행을 계속한다.
명령어 | 설명 |
bt | record 된 시점에서 백트레이스 목록을 출력 |
register | record 된 시점에서 레지스터 목록을 출력 |
thread | record 된 시점에서 쓰레드 상태와 주소값을 출력 |
mem dump [address] | 특정 메모리 영역을 출력 |
bl | 현재 시점에서 수행할 수 있는 로그 데이터 리스트 출력 |
go | 트랩이 발생하기 전까지 프로그램 수행 |
Record Mechanism
record 는 소프트웨어 수행 도중에 이벤트(s/w interrupt, h/w interrupt) 가 발생할 때마다 수행되며, 특정 메모리 영역에 저장된다.
이때 record 되는 정보는 다음과 같다.
- 이벤트 종류(s/w or h/w)
- 이벤트 발생 순서
- pc register
이벤트 로그(log) 데이터는 크게 이벤트 종류와 발생 순서를 나타내는 identification 부분과 이벤트가 발생한 지점을 나타내는 location 부분으로 나눠진다.
각 부분은 32 bit 를 사용하여 나타내고, 하나의 이벤트 로그 데이터는 총 8 byte 를 사용한다.
identification 부분은 발생한 이벤트의 종류, 발생한 순서를 나타낸다.
각 bit 는 발생한 이벤트가 s/w interrupt 인지 h/w interrupt 인지 구분하기 위해서, 각각 인터럽트마다 특정한 값을 사용하여 메모리에 저장한다.
s/w 인터럽트라면, 이벤트를 호출한 시스템 콜에 할당된 번호를 저장한다.
h/w 인터럽트의 경우에는, 인터럽트를 발생시킨 하드웨어에 할당된 번호를 저장한다.
location 부분은 이벤트가 발생한 시점의 pc register 값을 저장한다.
하지만, pc register 값 만으로는 재귀함수나 루프문이 수행되는 구조의 프로그램 루틴에서 이벤트가 발생하는 정확한 지점을 탐지하기 어렵다. 이러한 문제점을 해결하기 위해서 이벤트의 발생 순서를 저장함으로서, replay 시에 정확한 이벤트 지점을 찾는데 사용한다.
프로그램이 수행되는 동안에 record 되어지고, 수행 도중에 익셉션이나 수행이 종료되면 메모리에 저장된 로그(log) 데이터를 플래시 메모리에 저장한다.
Replay Mechanism
타겟을 재부팅하면, 최소한의 초기화(레지스터 설정, 각 모드별 Stack 초기화, gpio, uart)가 완료된 시점에서 record 모드로 부팅을 할 것인지, 아니면 replay 모드로 부팅할 것인지 선택한다.
replay 를 수행하기 위해서는, 타겟을 재부팅 시키고 replay 모드를 선택한다.
replay 모드에서는 모든 인터럽트가 disable 된다.
EVENT SELECT > [1] record mode [2] replay mode
번호 | 설명 |
1 | 프로그램 동작시에 발생하는 이벤트를 특정한 메모리 공간에 저장한다 |
2 | 저장된 로그(log) 데이터를 바탕으로 replay 를 수행한다 |
replay mode 를 선택하면, 기존의 플래시 메모리에 저장되어 있던 이벤트 로그(log) 데이터를 특정 메모리 영역으로 복사한다. 그리고 각각의 저장된 이벤트 로그 데이터를 읽어들여서 아래와 같이 리스트로 출력한다.
select ? [1] [S] pc = 0x30000234 // [S] 는 s/w interrupt [2] [T] pc = 0x30001BDC // [T] 는 h/w timer interrupt [3] [S] pc = 0x30000356
리스트 번호를 선택하면, 해당 이벤트가 발생했던 지점(pc register 값) 에 현재 저장되어 있는 명령어 코드를 메모리의 특정 영역에 저장한다.
그리고 나서, 트랩(trap)을 발생시킬 수 있는 명령어 코드를 삽입한다. 프로그램을 수행 시키면 이벤트가 발생했던 지점에서 트랩을 발생하게 되고, 여기서 디버깅 명령어를 사용할 수 있는 디버깅 모드로 진입하게 된다.
'go' 명령을 실행하면, 이벤트(s/w interrupt or h/w interrupt)를 수행하고 앞에서 메모리 특정 영역에 저장되어 있던 원래의 명령어코드를 다시 삽입하고 이 코드 부터 프로그램이 수행된다.
구현 및 실험
- 여러가지 형태의 realtime 프로그램을 디버깅하는 과정을 통해 검증
- 다른 RTOS(uc/os) 에 논문에서 제시한 방법 적용
- 유사한 record & replay 기반의 툴(jockey) 과 속도와 파일 크기 비교
Conclusion
추가할 사항
- 실험에서 jockey 와 record & replay 기법에서의 체크포인트 파일 크기 비교
- 실험에서 스택 오버플로우, 공유변수를 이용한 버그가 포함된 프로그램을 사용하여 디버깅하는 예제 추가
- 명령어에서 쓰레드의 스택과 힙 사용량 출력
- 익셉션 발생시에 발생하기 전까지 일어난 이벤트(s/w, h/w 인터럽트)의 로그를 보여줌
- 개발자가 개발시에 디버깅을 위해서 별도의 코드를 추가함으로서 발생하는 probe effect 가 발생
생각해 볼 사항
- record 기준을 인터럽트 기준으로만 할 것인가? 아니면, 애플리케이션 개발자도 사용할 수 있는 시스템 콜 형태로도 지원을 할 것인가?
- 관련 연구 내용이 적당한가?
- 좀 더 다양한 실험이 필요한가?(컴파일러 버전, 아키텍처 등)
- Content-based record 방법과 Ordering-based record 방법을 모두 사용한다. 일반적으로는 Content-based 방법을 사용하고, 공유 변수에 대해서는 Ordering-based 방법을 사용한다.
확인할 사항
- 인터럽트 enable/disable 동작 확인