프로그래머에게 디버거 사용은 무척이나 중요하다.
필자도 1,2 학년때는 그냥 다 printf를 이용하여 머리속으로 구조를 그려가며 프로그램을 짜고 있었다.
근데 다른 실무자들에게 여러 이야기를 들어보고, 이야기를 들어보면 디버거 사용이야말로 컴퓨터 공학과만의 주어진 권한이랄까. 아니 권한이란말보다는 선물이라는 표현이 더 괜찮겠다.
디버거를 사용하면 오류 찾기가 너무나 쉬워진다.
후에 좀더 큰 프로그램이나, 큰 프로젝트를 하면 일일이 printf를 통해서는 불가능한 곳도 있고 많은 시간이 소요된다.
보통 프로그램을 짜면 만드는데 40% 오류잡는데 60%란다.. ㅠ.ㅜ;
때로는 컴파일만 15분 된적도 있었다....(링크도 포함해서 수행하고 한 1G짜리 코드였음...)
그럴때는 확실히 디버거를 이용하거나 trace라는 명령어로 진행창에 실시간으로 해당값을 볼 수 있는 방법을 하면 편하다.
이럭저럭 필자도 디버거를 조금씩 사용에 적응 하고 있다. 디버거는 툴마다 다른데,
이 어셈블리어에서 사용할 디버거는 Gdb라는 GNU 소프트웨어 시스템을 위한 기본 디버거이다.
다양한 유닉스기반 시스템에서 동작하고 Alpha, ARM, H8/300, System/370, System 390, X86, IA-64 "아이태니엄", 모토로라 68000, MIPS, PA-RISC, PowerPC, SuperH, SPARC, VAX 을 지원하고 있다.
사용법도 은근히 복잡하고 적응 안되는 것들이 한두개가 아니다.
여기서 언급할 부분은 브레이크 포인트를 잡고, 돌리면서 변수값, 즉 레지스터내의 값을 확인하는 정도까지 설명하겠다.
이렇게 하면 printf등을 사용하지 않아도 레지스터 값을 체크해가면서 상태를 체크할 수 있다.
같이 사용한다면 어셈블러를 공부하는데 엄청 유익할 것이라고 생각한다.
GDB를 돌리면서 사용할 코드는 다음과 같다.
[작성환경 : solaris sparc machine 작성툴 : Vi editor]
위 코드는 (x-1)*(x-7)/(x-11)=y를 구현한 것으로 x값이 0 ~ 17까지 변할 때의 y 값을 출력한다.
출력값을 확인한다면
다음과 같이 [x y] 형식으로 출력된다.
이에 printf를 통한 출력이 아닌, Gdb를 통해 값을 확인해보자.
그전에 위 프로그램에 대한 간단한 개괄이다.
Gdb를 사용하기전에 꼭 해야할일이 있다.
그것은 컴파일 할때 꼭 -g 옵션을 주어야 한다는 것이다. 이는 심볼 정보보고를 한다는 옵션이다.
필자도 1,2 학년때는 그냥 다 printf를 이용하여 머리속으로 구조를 그려가며 프로그램을 짜고 있었다.
근데 다른 실무자들에게 여러 이야기를 들어보고, 이야기를 들어보면 디버거 사용이야말로 컴퓨터 공학과만의 주어진 권한이랄까. 아니 권한이란말보다는 선물이라는 표현이 더 괜찮겠다.
디버거를 사용하면 오류 찾기가 너무나 쉬워진다.
후에 좀더 큰 프로그램이나, 큰 프로젝트를 하면 일일이 printf를 통해서는 불가능한 곳도 있고 많은 시간이 소요된다.
보통 프로그램을 짜면 만드는데 40% 오류잡는데 60%란다.. ㅠ.ㅜ;
때로는 컴파일만 15분 된적도 있었다....(링크도 포함해서 수행하고 한 1G짜리 코드였음...)
그럴때는 확실히 디버거를 이용하거나 trace라는 명령어로 진행창에 실시간으로 해당값을 볼 수 있는 방법을 하면 편하다.
이럭저럭 필자도 디버거를 조금씩 사용에 적응 하고 있다. 디버거는 툴마다 다른데,
이 어셈블리어에서 사용할 디버거는 Gdb라는 GNU 소프트웨어 시스템을 위한 기본 디버거이다.
다양한 유닉스기반 시스템에서 동작하고 Alpha, ARM, H8/300, System/370, System 390, X86, IA-64 "아이태니엄", 모토로라 68000, MIPS, PA-RISC, PowerPC, SuperH, SPARC, VAX 을 지원하고 있다.
사용법도 은근히 복잡하고 적응 안되는 것들이 한두개가 아니다.
여기서 언급할 부분은 브레이크 포인트를 잡고, 돌리면서 변수값, 즉 레지스터내의 값을 확인하는 정도까지 설명하겠다.
이렇게 하면 printf등을 사용하지 않아도 레지스터 값을 체크해가면서 상태를 체크할 수 있다.
같이 사용한다면 어셈블러를 공부하는데 엄청 유익할 것이라고 생각한다.
GDB를 돌리면서 사용할 코드는 다음과 같다.
[작성환경 : solaris sparc machine 작성툴 : Vi editor]
위 코드는 (x-1)*(x-7)/(x-11)=y를 구현한 것으로 x값이 0 ~ 17까지 변할 때의 y 값을 출력한다.
출력값을 확인한다면
다음과 같이 [x y] 형식으로 출력된다.
이에 printf를 통한 출력이 아닌, Gdb를 통해 값을 확인해보자.
그전에 위 프로그램에 대한 간단한 개괄이다.
가장 간단한 1개의 루프로 구성되어 있으며 루프를 돌때마다 연산을 수행하고 값을 출력한다.
카운트는 총 18회를 한다.(즉 18회 순환) 수치는 모두 정수취급을 한다.
단 나머지의 경우 에러처리를 해준다.(11의 경우 제수가 0이되면 arithmetic Error가 발생한다.)
Lable은 loop와 exception을 중심으로 돈다.(fmt의 경우 printf를 위함임),nop은 최소화한 상태이다.
18~26사이가 연산을 구현한 코드이고 27~30이 출력코드, 22~23은 예외처리 부분, 32~34는 x의 값증가및 루프 카운트부분이다.
처음 보는 부분은 아마 cmp, subcc, set, ble, be,a등이 있을 것이다.
모두 Conditoon Code와 분기문등에 관한 내용이다. 여기서는 Gdb를 목적으로 포스팅을 하므로 그 부분은 다음 장에서 다룰 것이다.
카운트는 총 18회를 한다.(즉 18회 순환) 수치는 모두 정수취급을 한다.
단 나머지의 경우 에러처리를 해준다.(11의 경우 제수가 0이되면 arithmetic Error가 발생한다.)
Lable은 loop와 exception을 중심으로 돈다.(fmt의 경우 printf를 위함임),nop은 최소화한 상태이다.
18~26사이가 연산을 구현한 코드이고 27~30이 출력코드, 22~23은 예외처리 부분, 32~34는 x의 값증가및 루프 카운트부분이다.
처음 보는 부분은 아마 cmp, subcc, set, ble, be,a등이 있을 것이다.
모두 Conditoon Code와 분기문등에 관한 내용이다. 여기서는 Gdb를 목적으로 포스팅을 하므로 그 부분은 다음 장에서 다룰 것이다.
Gdb를 사용하기전에 꼭 해야할일이 있다.
그것은 컴파일 할때 꼭 -g 옵션을 주어야 한다는 것이다. 이는 심볼 정보보고를 한다는 옵션이다.
위에서 처럼 asdf 라는 실행파일을 생성하고 -g 옵션을 주었다. 이제 Gdb를 실행시키자.
실행시 명령은 gdb [실행파일명]을 치면 된다. [*.s]파일이 아닌 실행파일 명임을 기억하자. 그럼 위와같이 GDB정보가 나오며 (gdb)프롬프트 창이 뜬다. 이제 gdb상의 명령으로 수행해 나가면 된다.
gdb에 들어가서 필자가 가장 처음에 사용하는 처음 명령어는 바로 위의 display이다. 이것은 해당 위치의 명령어와 주소값, 상대위치등을 계속 확인할 수 있다. 후에 x를 통해 확인하는데 그때 다른 옵션을 줄필요가 없다.
그다음에는 여타 디버거처럼 BreakPoint를 잡아주면 된다. BreakPoint잡는 방법은 b [Lable or 위치]를 사용한다.
b는 breakpoint라는 뜻이고 오른쪽은 main으로부터 64주소만큼 떨어져 있는 곳을 breakpoint로 잡는다는 뜻이다.
그냥 단순히 레이블의 처음 값을 잡기 위해서는 b main으로만도 가능하나 각 레이블에서 떨어진 주소값은 위와 같이 *&를 꼭 레이블 앞에 붙인후 + 연산을 해주어야 한다.
Breakpoint가 0x106ec라는 곳에 하나 설정되었다는 확인 메시지가 출력된 것을 볼 수 있다.
이제 본격적으로 해당 레지스터에 어떤 값이 있는지 알아보자.
필자는 위 코드에서 한번 반복이 된 마지막 부분인 loop+68에다가 breakPoint를 잡았다. 즉 다음 루프를 돌기 바로 직전, branch하기 바로 직전상태이다.
breakpoint로 멈출부분을 설정했으면 이제 실행하면 된다. 프로그램을 실행하는 명령은 [r]로 run의 약자이다.
printf문이 실행되고 바로 멈춘 부분임을 확일할 수 있다. 현재 레지스터 값을 확인하기 위해 필자는 두번정도 loop를 더 돌렸다.
[c]명령어는 continue의 약자로 계속 진행한다는 의미이다. 즉 c명령어를 사용하면 다음 breakpoint까지 실행하게 된다.
실행할때마다 x,y의 값들을 눈으로도 확인할 수 있다.
이제 %i0와 %i1레지스터에는 각각 값들이 저장되어있을 것이다. 현재로서는 i0에 4이 i1에는 1이 저장되었을 것이다. 이를 확인해 보자. i0에 3이 아닌 4가저장된 이유는 코드상에서 루프를 통해 빠져나가기전 이미 inc로 1이 증가되었기 때문이다.
예를 들자면 for(i=0;i<10;i++) 에서 i++이 실행된것이다. (필자가 그렇게 breakPoint를 잡았다....)
위에서 언급한 바와 같이 레지스터의 값들을 확인할 수 있다.
이런식으로 gdb를 사용하여 디버깅한다면 assembler에서의 디버깅도 수월하게 할 수 있다.
[BreakPoint]를 기준으로 에러났을 때도 나름 추론할 수 있는 에러메시지를 출력해준다.
예를 들어 나눗셈에서 0으로 나누게 될 경우에는 Arithmetic에러를 출력하여 준다.^^~
※ 추가 : s(step)와 n(next)명령어는 한라인씩 실행하는 명령어로 s 5의 경우 5라인을 실행하는 것이다. s의 경우 함수를 만나면 함수 내부로 들어가 한칸씩 실행하지만, n의 경우 함수를 실행한 다음으로 바로 이동한다.
실행시 명령은 gdb [실행파일명]을 치면 된다. [*.s]파일이 아닌 실행파일 명임을 기억하자. 그럼 위와같이 GDB정보가 나오며 (gdb)프롬프트 창이 뜬다. 이제 gdb상의 명령으로 수행해 나가면 된다.
gdb에 들어가서 필자가 가장 처음에 사용하는 처음 명령어는 바로 위의 display이다. 이것은 해당 위치의 명령어와 주소값, 상대위치등을 계속 확인할 수 있다. 후에 x를 통해 확인하는데 그때 다른 옵션을 줄필요가 없다.
그다음에는 여타 디버거처럼 BreakPoint를 잡아주면 된다. BreakPoint잡는 방법은 b [Lable or 위치]를 사용한다.
b는 breakpoint라는 뜻이고 오른쪽은 main으로부터 64주소만큼 떨어져 있는 곳을 breakpoint로 잡는다는 뜻이다.
그냥 단순히 레이블의 처음 값을 잡기 위해서는 b main으로만도 가능하나 각 레이블에서 떨어진 주소값은 위와 같이 *&를 꼭 레이블 앞에 붙인후 + 연산을 해주어야 한다.
Breakpoint가 0x106ec라는 곳에 하나 설정되었다는 확인 메시지가 출력된 것을 볼 수 있다.
※ 여기서 혹시 코드의 위치를 자세히 보고 싶다면 [x]명령어를 사용하면 된다. 이 명령어는 examine의 약자로 조사한다는 의미로 외우면 쉽게 외워진다. 사용법은 다음과 같다.
위에서는 [x/12i loop] 에서 x가 명령어이고 나머지는 옵션인데 한마디로 loop라는 레이블을 기준으로 12개의 instruction을 조사해달라는 메시지이다. 레이블은 코드상에 본인이 설정한 레이블이고 한 명령어당 +4를 가며 이루어 지게된다. b 명령어를 이용하여 breakpoint를 설정할때 위 화면에서 나온 <loop+?> 값을 이용하여 잡는 것이 편하다. 즉 브레이크 포인트 잡을 장소를 x명령어로 확인하고 b로 포인트를 잡는 것이 편하다.(암산으로 breakPoint잡아도 무방하다^^)
위에서는 [x/12i loop] 에서 x가 명령어이고 나머지는 옵션인데 한마디로 loop라는 레이블을 기준으로 12개의 instruction을 조사해달라는 메시지이다. 레이블은 코드상에 본인이 설정한 레이블이고 한 명령어당 +4를 가며 이루어 지게된다. b 명령어를 이용하여 breakpoint를 설정할때 위 화면에서 나온 <loop+?> 값을 이용하여 잡는 것이 편하다. 즉 브레이크 포인트 잡을 장소를 x명령어로 확인하고 b로 포인트를 잡는 것이 편하다.(암산으로 breakPoint잡아도 무방하다^^)
b를 이용하여 break point를 잡을 때 다음을 고려해야 한다.
다음 두개의 차이점은 무엇일까?
각각 main과 loop라는 레이블에 BreakPoint를 걸어놓았다. 그러나 잡힌 주소가 다르다.
b main의 경우, main부분이 아닌 main+4부분이 Breakpoint로 설정이 되었고, b loop의 경우 loop의 처음부분이 제대로 잡혔다. 이는 b main의 코드가 sp(stack point)를 셋팅하는 부분으로 프로그램상일부로 접근하지 못하도록 breakpoint를 잡지 못하게 한것이라고 한다. 호기심에 b *&main+4 를 실행한다면(즉 main의 save 다음을 잡으면 어떻게 될까하시는분) 그대로 main+4위치가 잡혀 두개의 breakpoint가 main+4에 잡힌 상태가 된다.
다음 두개의 차이점은 무엇일까?
각각 main과 loop라는 레이블에 BreakPoint를 걸어놓았다. 그러나 잡힌 주소가 다르다.
b main의 경우, main부분이 아닌 main+4부분이 Breakpoint로 설정이 되었고, b loop의 경우 loop의 처음부분이 제대로 잡혔다. 이는 b main의 코드가 sp(stack point)를 셋팅하는 부분으로 프로그램상일부로 접근하지 못하도록 breakpoint를 잡지 못하게 한것이라고 한다. 호기심에 b *&main+4 를 실행한다면(즉 main의 save 다음을 잡으면 어떻게 될까하시는분) 그대로 main+4위치가 잡혀 두개의 breakpoint가 main+4에 잡힌 상태가 된다.
이제 본격적으로 해당 레지스터에 어떤 값이 있는지 알아보자.
필자는 위 코드에서 한번 반복이 된 마지막 부분인 loop+68에다가 breakPoint를 잡았다. 즉 다음 루프를 돌기 바로 직전, branch하기 바로 직전상태이다.
breakpoint로 멈출부분을 설정했으면 이제 실행하면 된다. 프로그램을 실행하는 명령은 [r]로 run의 약자이다.
printf문이 실행되고 바로 멈춘 부분임을 확일할 수 있다. 현재 레지스터 값을 확인하기 위해 필자는 두번정도 loop를 더 돌렸다.
[c]명령어는 continue의 약자로 계속 진행한다는 의미이다. 즉 c명령어를 사용하면 다음 breakpoint까지 실행하게 된다.
실행할때마다 x,y의 값들을 눈으로도 확인할 수 있다.
이제 %i0와 %i1레지스터에는 각각 값들이 저장되어있을 것이다. 현재로서는 i0에 4이 i1에는 1이 저장되었을 것이다. 이를 확인해 보자. i0에 3이 아닌 4가저장된 이유는 코드상에서 루프를 통해 빠져나가기전 이미 inc로 1이 증가되었기 때문이다.
예를 들자면 for(i=0;i<10;i++) 에서 i++이 실행된것이다. (필자가 그렇게 breakPoint를 잡았다....)
위에서 언급한 바와 같이 레지스터의 값들을 확인할 수 있다.
이런식으로 gdb를 사용하여 디버깅한다면 assembler에서의 디버깅도 수월하게 할 수 있다.
[BreakPoint]를 기준으로 에러났을 때도 나름 추론할 수 있는 에러메시지를 출력해준다.
예를 들어 나눗셈에서 0으로 나누게 될 경우에는 Arithmetic에러를 출력하여 준다.^^~
※ 추가 : s(step)와 n(next)명령어는 한라인씩 실행하는 명령어로 s 5의 경우 5라인을 실행하는 것이다. s의 경우 함수를 만나면 함수 내부로 들어가 한칸씩 실행하지만, n의 경우 함수를 실행한 다음으로 바로 이동한다.
'무언가 만들기 위한 지식 > SPARC Assembler' 카테고리의 다른 글
SPARC Assembler Memory Map (2) | 2010.04.19 |
---|---|
SPARC Assembler File i/o (input/output) Trap Service_파일입출력 (1) | 2010.04.19 |
SPARC Assembler Switch (4) | 2010.04.09 |
SPARC Assembler Argument(about argc, argv)_인자에 대해서 (1) | 2010.04.09 |
SPARC Assembler Array Control (0) | 2010.04.09 |