Debugger
Windows Visual Studio
Debugging 이란 프로그램의 논리적 오류를 찾아내서 수정하는 일이며, 이 작업을 도와 주는 툴을 Debugger라고 한다. 즉 프로그램의 논리적 오류를 검출하는데 사용하는 프로그램이 Debugger이다. 윈도우 환경에서 우리가 설치한 Visual Studio 6에는 Debugger가 포함되어 있는데, 간단한 사용법을 알아 보았다.
1. 일단 Debugging 해볼 간단한 코드를 작성해 보았다.
2. 코드를 작성하고 F7을 눌려 Build를 한다.
3. Debugging 할 부분에 F9를 눌러 Breakpoint를 건다. 밑의 그림과 같이 빨간색 점이 생긴다.
4. F5를 눌러 Debugging을 시작한다. 그러면 프로그램이 실행되고 Breakpoint에서 실행을 멈춘다.
위 그림에서 화살표는 현재 코드를 실행할 차례라는 것을 의미한다. 왼쪽 밑에 보면 Auto라는 탭이 있는데 여기는 Debugger가 알아서 변수를 등록시켜서 그 값들을 출력해준다. 현재 iNum변수가 초기화 되지 않아 쓰레기 값을 가지고 있는 것을 알 수 있다.
5. Debug 메뉴를 보면 step over, step into라는 명령이 있다. step over(F10)는 현재 행을 실행하되 블록 안으로 들어가지 않는 명령어이고, step into(F11)는 현재 행을 실행하고 블록 안으로 들어가는 명령어 이다. 여기선 iNum값들을 확인하기 위해서 F11(step into)를 눌러 진행한다.
위 그림을 보면 for문이 처음 실행되고 iNum값이 0으로 설정된 것을 볼 수 있다. 이런 식으로 변수 값들을 직접 눈으로 확인하면서 프로그램의 오류를 검출한다. 또한 오른쪽 밑의 watch에 따로 변수를 등록할 수도 있다.
오른쪽 밑 watch에 &iNum을 등록해서 주소값과 실제 가지고 있는 값을 다 알 수 있다.
Linux Debugger GDB
Linux에도 강력한 Debugger GDB가 있다. GDB는 텍스트 기반 Debugger로 많은 명령어들이 있다
l(list) |
소스 보기 |
b |
브레이크 포인트 |
info breakpoints |
브레이크 포인트 정보(info b) |
cl |
브레이크 포인트 삭제 |
r(run) |
프로그램 수행 |
s(step) |
프로그램을 한 행씩 실행(함수 호출 시 내부로 들어감) |
n(next) |
프로그램을 한 행씩 실행(함수 호출 시 내부로 들어가지 않음) |
c(continue) |
브레이크 포인트를 만날 때까지 실행 |
u |
현재 루프를 빠져 나감 |
finsh |
현재 함수를 수행하고 빠져나감 |
return |
현재 함수를 수행하지 않고 빠져나감 |
si |
인스트럭션 단위로 s동작 실행 |
ni |
인스트럭션 단위로 n동작 실행 |
watch |
변수 값이 변할 때마다 알려줌 |
info locals |
현재 상태에서 지역 변수들을 보여줌 |
info variable |
현재 상태에서 전역 변수들을 보여줌 |
p(print) |
해당 변수나 함수 값을 보여준다(밑에 표 참조) |
p 변수=설정값 |
변수 값 설정 |
display |
변수 값을 계속 보여줌(밑에 표 참조) |
info frame |
현재 함수의 스택 프레임을 보여줌 |
x |
메모리 상태 검사(x/[범위][출력 형식] [범위의 단위]) |
disas |
어셈블리 코드 보기 |
call |
함수를 임의대로 호출 할 수 있다 |
signal |
프로세스에게 시그널을 보낸다 |
set |
메모리 특정 영역에 값을 설정(set {타입}[주소] = [값]) |
p [변수명] |
변수 값을 출력 |
p [함수명] |
함수의 주소를 출력 |
p/[출력 형식] [변수명] |
변수 값을 출력 형식으로 출력 |
p '[파일명]'::[변수명] |
파일명에 있는 전역 변수의 값을 출력 |
p [함수명]::[변수명] |
함수에 있는 변수 값을 출력 |
p [변수명]@[배열 크기] |
변수의 내용을 변수 배열의 크기 형태로 출력 |
display [변수명] |
변수 값을 매번 화면에 디스플레이 한다 |
display/[출력 형식] [변수명] |
변수 값을 출력 형식으로 디스플레이 한다 |
undisplay [디스플레이 번호] |
디스플레이 설정을 없앤다 |
disable display [디스플레이 번호] |
디스플레이를 일시 중단한다 |
enable display [디스플레이 번호] |
디스플레이를 다시 활성화한다 |
t |
2진수로 출력 |
o |
8진수로 출력 |
d |
부호가 있는 10진수로 출력(int) |
u |
부호가 없는 10진수로 출력(unsigned int) |
x |
16진수로 출력 |
c |
최소 1바이트 값을 문자형으로 출력 |
f |
부동 소수점 값 형식으로 출력 |
a |
가장 가까운 심볼의 오프셋을 출력 |
기본적인 명령어는 다음과 같다. 위와 같은 코드를 리눅스에서 GDB를 이용하여 Debugging 해보았다.
1. GDB에서 Debugging을 하기 위해서는 일단 컴파일 할 때 옵션 -g를 주어야 한다. 이 옵션은 Debugging정보를 넣고, 모든 최적화 flag를 turn off 한다.
2. "$ gdb 실행파일명" 으로 GDB를 실행한다.
3. list 명령으로 소스코드를 볼 수 있다
4. b 명령으로 7번째 줄에 Breakpoint를 건다. 그리고 r명령을 눌러 실행하면 7번째 줄에서 실행을 멈춘다.
5. display명령으로 iNum의 상태를 계속 확인 할 수 있다. s 명령으로 계속 소스코드를 실행시켜 보면서 iNum값을 확인 할 수 있다.
일단 GDB를 이용하여 간단하게 Debugging을 해보았다. 윈도우 시스템에서와 달리 for문에 아무 명령문이 없었을 때, 리눅스에서는 i 값을 바로 5로 바꿔버려서 iNum값의 변화를 알 수가 없었다. 그래서 printf("test\n"); 명령문을 넣어 iNum값을 확인 할 수 있게 하였다.
LED ON/OFF 일반화
main.c
#include
"port.h"
#define LED_ON(led) PORTF &= (~(0x01 << led)) // led번째LED ON
#define LED_OFF(led) PORTF |= (0x01 << led) // led번째LED OFF
int main(void)
{
unsigned
int us = 0;
unsigned
int ms = 0;
unsigned
char led = 0x00;
DDRF = 0xff;
PORTF = 0x00;
while(1)
{
us++;
if(us >= 1000)
{
ms++;
us = 0;
}
if(ms >= 500)
{
if(led == 8)
{
led = 0;
PORTF = 0x00;
}
LED_OFF(led);
// LED_ON(led);
led++;
ms = 0;
}
}
return 0;
}
#define LED_ON(led) PORTF &= (~(0x01 << led))
LED를 ON시키려면 Low 신호를 주면 된다. 어떤 수든 상관없이 Low를 만들려면 0을 AND연산하면 되므로 다음과 같이 원하는 비트만큼 0x01를 <<연산한 다음에 보수를 취해서 AND연산을 하면 ON시킬 수 있다.
#define LED_OFF(led) PORTF |= (0x01 << led)
LED를 OFF 시키려면 High 신호를 주면 된다. 어떤 수든 상관없이 High를 만들려면 1을 OR연산하면 되므로 다음과 같이 원하는 비트만큼 0x01를 <<연산한 다음 OR연산을 하면 OFF 시킬 수 있다.