C++에 대한 기본 디버깅 태스크에 대한 설명은 다음 C++ 프로그램을 참조하십시오.
이 절에서 설명하는 자료와 연관된 자세한 정보에 대해서는 다음 주제를 참조하십시오.
아래 프로그램은 디버깅 태스크를 보여 주기 위해 다양한 주제에서 사용됩니다.
이 프로그램은 문자 버퍼에서 입력을 읽는 단순한 계산기입니다. 정수를 읽은 경우에는 스택에 밀어넣어집니다. 연산자(+ - * /) 중 하나를 읽은 경우에는 맨 위에 있는 두 요소가 스택에서 나가고, 해당 연산자에 대해 연산이 수행된 다음 그 결과가 스택에 밀어넣어집니다. = 연산자는 스택의 맨 위에 있는 요소의 값을 버퍼에 씁니다.
CALC.HPP
/*----- FILE CALC.HPP ------------------------------------------------*/
/* */
/* Header file for CALC.CPP PUSHPOP.CPP READTOKN.CPP */
/* a simple calculator */
/*--------------------------------------------------------------------*/
typedef enum toks {
T_INTEGER,
T_PLUS,
T_TIMES,
T_MINUS,
T_DIVIDE,
T_EQUALS,
T_STOP
} Token;
extern "C" Token read_token(char buf[]);
class IntLink {
private:
int i;
IntLink * next;
public:
IntLink();
~IntLink();
int get_i();
void set_i(int j);
IntLink * get_next();
void set_next(IntLink * d);
};
class IntStack {
private:
IntLink * top;
public:
IntStack();
~IntStack();
void push(int);
int pop();
};
CALC.CPP
/*----- FILE CALC.CPP ------------------------------------------------*/
/* */
/* A simple calculator that does operations on integers that */
/* are pushed and popped on a stack */
/*--------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include "calc.hpp"
IntStack stack;
int main()
{
Token tok;
char word[100];
char buf_out[100];
int num, num2;
for(;;)
{
tok=read_token(word);
switch(tok)
{
case T_STOP:
break;
case T_INTEGER:
num = atoi(word);
stack.push(num); /* CALC1 statement */
break;
case T_PLUS:
stack.push(stack.pop()+stack.pop());
break;
case T_MINUS:
num = stack.pop();
stack.push(num-stack.pop());
break;
case T_TIMES:
stack.push(stack.pop()*stack.pop() );
break;
case T_DIVIDE:
num2 = stack.pop();
num = stack.pop();
stack.push(num/num2); /* CALC2 statement */
break;
case T_EQUALS:
num = stack.pop();
sprintf(buf_out,"= %d ",num);
stack.push(num);
break;
}
if (tok==T_STOP)
break;
}
return 0;
}
PUSHPOP.CPP
/*----- FILE: PUSHPOP.CPP --------------------------------------------*/
/* */
/* Push and pop functions for a stack of integers */
/*--------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include "calc.hpp"
/*--------------------------------------------------------------------*/
/* input: num - value to push on the stack */
/* action: get a link to hold the pushed value, push link on stack */
/*--------------------------------------------------------------------*/
void IntStack::push(int num) {
IntLink * ptr;
ptr = new IntLink;
ptr–>set_i(num);
ptr–>set_next(top);
top = ptr;
}
/*--------------------------------------------------------------------*/
/* return: int value popped from stack (0 if stack is empty) */
/* action: pops top element from stack and get return value from it */
/*--------------------------------------------------------------------*/
int IntStack::pop() {
IntLink * ptr;
int num;
ptr = top;
num = ptr–>get_i();
top = ptr–>get_next();
delete ptr;
return num;
}
IntStack::IntStack() {
top = 0;
}
IntStack::~IntStack() {
while(top)
pop();
}
IntLink::IntLink() { /* constructor leaves elements unassigned */
}
IntLink::~IntLink() {
}
void IntLink::set_i(int j) {
i = j;
}
int IntLink::get_i() {
return i;
}
void IntLink::set_next(IntLink * p) {
next = p;
}
IntLink * IntLink::get_next() {
return next;
}
READTOKN.CPP
/*----- FILE READTOKN.CPP --------------------------------------------*/
/* */
/* A function to read input and tokenize it for a simple calculator */
/*--------------------------------------------------------------------*/
#include <ctype.h>
#include <stdio.h>
#include "calc.hpp"
/*--------------------------------------------------------------------*/
/* action: get next input char, update index for next call */
/* return: next input char */
/*--------------------------------------------------------------------*/
static char nextchar(void)
{
/* input action
* ----- ------
* 2 push 2 on stack
* 18 push 18
* + pop 2, pop 18, add, push result (20)
* = output value on the top of the stack (20)
* 5 push 5
* / pop 5, pop 20, divide, push result (4)
* = output value on the top of the stack (4)
*/
char * buf_in = "2 18 + = 5 / = ";
static int index; /* starts at 0 */
char ret;
ret = buf_in[index];
++index;
return ret;
}
/*--------------------------------------------------------------------*/
/* output: buf - null terminated token */
/* return: token type */
/* action: reads chars through nextchar() and tokenizes them */
/*--------------------------------------------------------------------*/
extern "C"
Token read_token(char buf[])
{
int i;
char c;
/* skip leading white space */
for( c=nextchar();
isspace(c);
c=nextchar())
;
buf[0] = c; /* get ready to return single char e.g. "+" */
buf[1] = 0;
switch(c)
{
case '+' : return T_PLUS;
case '-' : return T_MINUS;
case '*' : return T_TIMES;
case '/' : return T_DIVIDE;
case '=' : return T_EQUALS;
default:
i = 0;
while (isdigit(c)) {
buf[i++] = c;
c = nextchar();
}
buf[i] = 0;
if (i==0)
return T_STOP;
else
return T_INTEGER;
}
}
이 절에서 설명하는 자료와 연관된 자세한 정보에 대해서는 다음 주제를 참조하십시오.
이 주제에서는 AT CALL 또는 AT ENTRY 명령을 사용하여 루틴이 호출된 직후에 정지하는 방법을 설명합니다. 예제: 디버깅을 위한 샘플 C++ 프로그램은 이 명령을 설명하는 데 사용됩니다. 이 명령을 사용하기 전에 다음 태스크를 수행해야 합니다.
이 명령을 사용하기 전에 함수명과 함께 C++ 서명을 포함시키십시오.
중단점을 입력할 수 있게 설정하면 소스 창 맨 위 행에 있는 파일명 위에
PUSHPOP.CPP를 입력하여 소스 창에 PUSHPOP.CPP를 표시할 수 있습니다.
이렇게 하면 PUSHPOP.CPP가 현재 규정된 프로그램이 됩니다.
그러면 다음 명령을 입력할 수 있습니다.
LIST NAMES
Debug Tool은 현재 규정된 프로그램의 모든 블록 및 변수명을 표시합니다. Debug Tool은 로그 창에 다음 예제와 비슷한 정보를 표시합니다.
There are no session names. The following names are known in block CALC ::> "USERID.MFISTART.CPP(PUSHPOP)" IntStack::~IntStack() IntStack::IntStack() IntLink::get_i() IntLink::get_next() IntLink::~IntLink() IntLink::set_i(int) IntLink::set_next(IntLink*) IntLink::IntLink()
이제 블록명 옆에 명령을 삽입하여 키 입력을 줄일 수 있습니다.
IntStack::push(int)가 호출되기 직전에 정지하려면 함수 서명 옆에 AT CALL을 삽입한 다음 Enter를 누르십시오. 그러면 명령행에 전체 명령이 놓입니다. 이제 명령행의 AT CALL IntStack::push(int)를 사용하여 다음 명령을 입력할 수 있습니다.
AT CALL IntStack::push(int)
IntStack::push(int)가 호출된 직후에 정지하려면 AT CALL 명령과 같은 방식으로 다음 명령을 입력하십시오.
AT ENTRY IntStack::push(int) ;
IntStack::push(int)가 호출된 직후에 num이 16인 경우에만 정지하려면 다음 명령을 입력하십시오.
AT ENTRY IntStack::push(int) WHEN num=16;
단일 변수의 컨텐츠를 나열하려면 변수명으로 커서를 이동하고 PF4(LIST)를 누르십시오. 로그 창에 값이 표시됩니다. 이는 명령행에 LIST TITLED variable을 입력하는 것과 같습니다.
CALC 프로그램을 실행하고 IntLink가 할당될 때까지 IntStack::push(int) 함수의 첫 번째 호출로 step into하십시오. 그런 후 다음 Debug Tool 명령을 입력하십시오.
LIST TITLED num
Debug Tool 로그 창에 다음 정보가 표시됩니다.
LIST TITLED num; num = 2
num 값을 22로 수정하려면 num = 2 행 위에 num = 22를 입력하고 Enter를 눌러 명령행에 놓은 다음 다시 Enter를 눌러 명령을 실행하십시오.
명령행에 대부분의 C++ 표현식을 입력할 수 있습니다.
IntStack::push(int)의 ptr 변수 속성을 보려면 다음 Debug Tool 명령을 실행하십시오.
DESCRIBE ATTRIBUTES *ptr;
로그 창에 다음과 같은 결과가 표시됩니다.
속성: * ptr
주소: 0BA25EB8 , 길이: 8
class IntLink
signed int i
struct IntLink *next따라서 이 명령을 사용하여 대부분의 클래스, 구조 및 유니온을 찾아볼 수 있습니다.
예를 들어, 다음 명령을 실행하여 ptr이 가리키는 클래스 오브젝트의 모든 데이터 멤버 값을 나열할 수 있습니다.
LIST *ptr ;
그러면 로그 창에 다음과 같은 결과가 표시됩니다.
LIST * ptr ; * ptr.i = 0 * ptr.next = 0x00000000
다음 예제에서와 같이 명령으로 대입을 실행하여 클래스 오브젝트의 데이터 멤버 값을 변경할 수 있습니다.
(* ptr).i = 33 ;
이 절에서 설명하는 자료와 연관된 자세한 정보에 대해서는 다음 주제를 참조하십시오.
대개 프로그램의 특정 부분이 처음에는 잘 작동하지만 특정 조건에서는 실패하는 경우가 많습니다. 이때 단순히 행 중단점만 설정하면 계속해서 GO를 입력해야 하므로 이 방법은 효율적이지 않습니다.
예를 들어, main에서 나누기가 0인 경우에만(예외가 발생하기 전에) T_DIVIDE에서 중지하려고 합니다. 다음과 같이 중단점을 설정하십시오.
AT 40 { if(num2 != 0) GO; }40행은 CALC2 명령문입니다. 이 명령을 실행하면 Debug Tool이 40행에서 중지합니다. num 값이 0이 아니면 프로그램이 계속 실행됩니다. num2가 0인 경우에만 Debug Tool이 40행에서 중지합니다.
예를 들어, IntLink 클래스의 클래스 메소드로 step into하면 다음 명령
LIST TITLED ;
가 this가 포함된 목록으로 응답합니다. 다음 명령
DESCRIBE ATTRIBUTES *this ;
를 사용하여 this 포인터가 가리키는 데이터 요소의 유형을 볼 수 있습니다. 다음 명령
LIST *this ;
를 사용하면 가리키는 오브젝트의 데이터 멤버가 나열되고
LIST * this ; (* this).i = 4 (* this).next = 0x0
과 같은 내용이 로그 창에 표시됩니다. 요소 i를 수정하려면 다음 명령
i = 2001;
을 입력하거나, 혼동할 우려가 있는 경우(예: i라는 자동 변수도 있는 경우)에는 다음 명령을 입력하십시오.
(* this).i = 2001 ;
PUSHPOP.CPP 파일의 IntStack::push(int) 함수에 대한 시작점에 중단점을 설정하려 한다고 가정해 보십시오. PUSHPOP.CPP는 TEST를 사용하여 컴파일되었지만 다른 파일은 그렇지 않습니다. Debug Tool에 빈 소스 창이 표시됩니다. 컴파일 단위를 표시하려면 다음 명령을 입력하십시오.
LIST NAMES CUS
LIST NAMES CUS 명령은 Debug Tool에 알려진 모든 컴파일 단위 목록을 표시합니다.
사용 중인 컴파일러에 따라 또는 USERID.MFISTART.CPP(PUSHPOP)가 애플리케이션에 의해 나중에 페치된 경우, 이 컴파일 단위가 Debug Tool에 알려졌을 수도 있고 알려지지 않았을 수도 있으며 PDS 멤버 PUSHPOP이 표시될 수도 있고 표시되지 않을 수도 있습니다. 표시되는 경우 다음 명령을 입력하십시오.
SET QUALIFY CU "USERID.MFISTART.CPP(PUSHPOP)" AT ENTRY IntStack::push(int) ; GO ;
또는
AT ENTRY "USERID.MFISTART.CPP(PUSHPOP)":>push GO
표시되지 않는 경우 다음과 같이 appearance 중단점을 설정해야 합니다.
AT APPEARANCE "USERID.MFISTART.CPP(PUSHPOP)" ; GO ;
다음과 같이 중단점을 결합할 수도 있습니다.
AT APPEARANCE "USERID.MFISTART.CPP(PUSHPOP)" AT ENTRY push; GO;
이 appearance 중단점은 PUSHPOP 컴파일 단위의 함수가 처음 실행될 때 제어를 가져오기 위해 필요합니다. 제어를 가져오면, 다음과 같이 IntStack::push(int)에 대한 시작점에 중단점을 설정할 수 있습니다.
AT ENTRY IntStack::push(int) ;
로그 창으로 stdout의 방향을 바꾸려면 다음 명령을 실행하십시오.
SET INTERCEPT ON FILE stdout ;
이 SET 명령을 사용하면 프로그램 뿐만 아니라 대화식 함수 호출에서도 stdout을 캡처할 수 있습니다. 예를 들어, 다음 명령을 입력하여 명령행에서 대화식으로 cout를 사용하면 널(null) 종료 문자열을 표시할 수 있습니다.
cout << sptr ;
이 방법을 사용하면 LIST STORAGE를 사용할 때보다 쉽습니다.
CICS®의 경우에만, SET INTERCEPT가 지원되지 않습니다.
명령 프롬프트에서 입력할 수 있도록 stdin 입력의 방향을 바꾸려면 다음 단계를 수행하십시오.
EQA1290I 프로그램이 stdin에서 입력 대기 중입니다.
EQA1292I INPUT 명령을 사용하여 인터셉트된 가변 형식 파일에
최대 1000 문자 이상을 입력할 수 있습니다. 이 절에서 설명하는 자료와 연관된 자세한 정보에 대해서는 다음 주제를 참조하십시오.
명령행에서 대화식으로 라이브러리 함수(예: strlen)나 프로그램 함수 중 하나를 호출하여 해당 함수를 시작할 수 있습니다. read_token과 같은 C 연계 함수도 시작할 수 있습니다. 그러나 C++ 연계 함수는 대화식으로 호출할 수 없습니다. 기능은 다음 요구사항을 따라야 합니다.
아래 예제에서는 read_token을 대화식으로 호출합니다.
AT CALL read_token; GO; read_token(word);
입력에서 추가 토큰이 제거되었으므로 계산기가 이전과는 다른 결과를 생성합니다.
char * 변수 ptr은 인쇄 가능 문자가 포함된 스토리지의 한 부분을 가리킬 수 있습니다. 처음 20자를 표시하려면 다음 명령을 입력하십시오.
LIST STORAGE(*ptr,20)
문자열이 널(null)로 종료되면 다음 예제에서와 같이 명령행에서 대화식 함수 호출을 사용할 수도 있습니다.
puts(ptr) ;
오프셋을 기준으로 스토리지를 표시할 수도 있습니다. 예를 들어, 위치 20CD0의 오프셋 2에서 10바이트를 표시하려면 다음 명령을 사용하십시오.
LIST STORAGE(0x20CD0,2,10);
이 절에서 설명하는 자료와 연관된 자세한 정보에 대해서는 다음 주제를 참조하십시오.
IntStack::push(int)와 IntStack::pop()을 내보내 DLL로 PUSHPOP.CPP를 빌드하십시오. PUSHPOP DLL에서 IntStack::push(int)와 IntStack::pop()을 가져오는 프로그램으로 CALC.CPP와 READTOKN.CPP를 빌드하십시오. CALC 애플리케이션이 시작될 때는 PUSHPOP DLL이 Debug Tool에 알려져 있지 않습니다. 다음 예제와 같이 AT APPEARANCE 중단점을 사용하여, 이 컴파일 단위의 코드가 처음 나타날 때 DLL에 대한 제어를 가져옵니다.
AT APPEARANCE "USERID.MFISTART.CPP(PUSHPOP)" ; GO ;
이 appearance 중단점은 PUSHPOP 컴파일 단위의 함수가 처음 실행될 때 제어를 가져오기 위해 필요합니다. 제어를 가져오면, PUSHPOP에 중단점을 설정할 수 있습니다.
대개 프로그래밍 오류가 발생하면 어떻게 프로그래밍 오류가 발생하게 되었는지, 특히 호출 함수의 역추적이 무엇인지 파악하려고 합니다. 이 정보를 보려면 다음 명령을 실행하십시오.
LIST CALLS ;
예를 들어, 다음 명령과 함께 CALC 예제를 실행하십시오.
AT ENTRY read_token ; GO ; LIST CALLS ;
그러면 로그 창에 다음과 같은 내용이 표시됩니다.
At ENTRY in C function "USERID.MFISTART.CPP(READTOKN)" :> read_token. From LINE 18 in C function "USERID.MFISTART.CPP(CALC)" :> main :> %BLOCK2.
이는 호출자의 역추적을 표시합니다.
프로그램을 변경할 필요없이 시작점과 종료점을 표시하여 프로그램을 추적하려면 아래 예제에서와 같이 파일에 다음 Debug Tool 명령을 배치한 다음 Debug Tool이 처음 프로그램을 표시할 때 이 명령을 사용하십시오. USERID.DTUSE(TRACE)와 다음 Debug Tool 명령이 포함된 데이터셋을 가지고 있다고 가정해 보십시오.
int indent;
indent = 0;
SET INTERCEPT ON FILE stdout;
AT ENTRY * { ₩
++indent; ₩
if (indent < 0) indent = 0; ₩
printf("%*.s>%s₩n", indent, " ", %block); ₩
GO; ₩
}
AT EXIT * {₩
if (indent < 0) indent = 0; ₩
printf("%*.s<%s₩n", indent, " ", %block); ₩
--indent; ₩
GO; ₩
}다음 명령을 입력하여 이 파일을 Debug Tool에 대한 명령 소스로 사용할 수 있습니다.
USE USERID.DTUSE(TRACE)
USE 파일을 실행하면 아래 나열된 프로그램의 실행에 대한 추적이 로그 창에 표시됩니다.
int foo(int i, int j) {
return i+j;
}
int main(void) {
return foo(1,2);
}로그 창의 다음 추적은 Debug Tool 명령에 대한 입력 소스로 USE 파일을 사용하여 샘플 프로그램을 실행한 후 표시됩니다.
>main >foo(int,int) <foo(int,int) <main
USE 파일을 작성하지 않으려면 명령행에서 명령을 입력하십시오. 이렇게 해도 동일한 결과가 나타날 수 있습니다.
프로그램이 실행되는 중에 일부 스토리지의 값이 예기치 않게 변경되어 이 값이 언제 어디서 변경되었는지 알아야 할 때가 있습니다. 다음과 같은 간단한 예제를 참조하십시오. 다음 예제에서는 호출자가 예상하는 것보다 set_i 함수가 더 많이 변경됩니다.
struct s { int i; int j;};
struct s a = { 0, 0 };
/* function sets only field i */
void set_i(struct s * p, int k)
{
p–>i = k;
p–>j = k; /* error, it unexpectedly sets field j also */
}
main() {
set_i(&a,123);
}다음 명령을 사용하여 a의 주소를 찾으십시오.
LIST &(a.j) ;
결과가 0x7042A04라고 가정해 보십시오. 이 주소에서 시작하여 다음 4바이트에서 스토리지 값이 변경되었는지 감시하는 중단점을 설정하려면 다음 명령을 실행하십시오.
AT CHANGE %STORAGE(0x7042A04,4)
이 스토리지의 값이 변경된 경우, 프로그램이 실행될 때 Debug Tool이 정지됩니다.
초기화되지 않은 스토리지 오류를 쉽게 찾으려면 Language Environment® TEST 런타임 옵션과 STORAGE 옵션을 사용하여 프로그램을 실행하십시오. 다음 예제를 참조하십시오.
TEST STORAGE(FD,FB,F9)
STORAGE의 첫 번째 하위 매개변수는 힙에서 할당된 스토리지에 대한 채우기 바이트입니다. 예를 들어, new 연산자를 통해 할당된 스토리지는 0xFD바이트로 채워집니다. 스토리지에서 이 바이트가 반복되면 초기화되지 않은 힙 스토리지일 수 있습니다.
STORAGE의 두 번째 하위 매개변수는 힙에서 할당된 다음 비워진 스토리지에 대한 채우기 바이트입니다. 예를 들어, delete 연산자를 통해 비워진 스토리지는 0xFB바이트로 채워질 수 있습니다. 스토리지에서 이 바이트가 반복되면 힙에 할당되었지만 비워진 스토리지일 수 있습니다.
STORAGE의 세 번째 하위 매개변수는 새 스택 프레임의 자동 스토리지 변수에 대한 채우기 바이트입니다. 스토리지에서 이 바이트가 반복되면 초기화되지 않은 자동 스토리지일 수 있습니다.
이 예제에서는 가능한 한 초기에 문제점을 발견할 수 있도록 큰 홀수 값을 선택했습니다. 예를 들어, 홀수 주소로 분기하려고 하면 즉시 예외가 발생합니다.
초기화되지 않은 스토리지 예제를 실행해 보겠습니다. STORAGE 런타임 옵션을 STORAGE(FD,FB,F9)로 지정하여 PUSHPOP2 행까지 CALC 프로그램을 실행한 후 다음 명령을 실행하십시오.
LIST *ptr ;
다음 예제와 같이 초기화되지 않은 힙 스토리지에 대한 바이트 채우기가 표시됩니다.
LIST *ptr ; (* ptr).next = 0xFDFDFDFD (* ptr).i = -33686019
이 절에서 설명하는 자료와 연관된 자세한 정보에 대해서는 다음 주제를 참조하십시오.
정의되지 않은 함수를 호출하거나 널(null)을 가리키는 함수 포인터를 통해 함수를 호출하면 심각한 오류가 발생할 수 있습니다. 이러한 호출이 실행되기 직전에 정지하려면 다음과 같이 중단점을 설정하십시오.
AT CALL 0
이 중단점에서 Debug Tool이 중지하면 GO BYPASS 명령을 입력하여 이 호출을 생략할 수 있습니다. 이렇게 하면 오류 조건을 발생시키지 않고 디버그 세션을 계속할 수 있습니다.