IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  Rational  >

IBM Rational Purify의 향상된 기능: Purify로 디버깅하기

Rational Purify API와 메모리 감시점은 메모리 오류를 빠르게 찾고 디버그하는 데 도움이 된다

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

Satish Chandra Gupta, 프로그래머, RAD/PurifyPlus, Rational Software, IBM Japan
Anand Gaurav, 프로그래머, PurifyPlus, IBM Japan

2008 년 4 월 08 일

IBM® Rational® Purify®는 분석과 수정이 매우 어려운 잘못된 메모리 오류를 정확히 찾아내는 도구입니다. 프로그램이 메모리를 어떻게 사용하는지 모니터링 및 분석하고, 오류의 원인과 위치를 정확히 지적하는 자세한 소스 코드와 함께 오류를 보고합니다. 이 글에서는 Rational Purify API와 메모리 오류를 능숙히 분석하는 디버거 및 감시점을 어떻게 사용하는지 소개합니다.

메모리 오류는 소프트웨어 오류 중 분석과 수정이 가장 어려운 분야 중 하나다. 잘못된 메모리 소스와 오류의 조작이 완전히 달라 원인과 결과를 연관시키기가 어렵기 때문이다. 더구나 비정상적인 상황에서 발생하는 오류는 일관성있게 재현하기가 힘들다. 전형적으로 이들 오류는 프로그램, 써드파티 라이브러리, 운영체제 내의 각기 다른 컴포넌트 사이의 복잡한 인터랙션에서 온 결과다. 소스 코드만 확인해서는 이런 가능성이나 시나리오를 예측하고 파악하기가 너무 어렵다. 메모리 오류를 일으키는 잘못된 프로그램 로직과 디자인을 이해하려면 보통 많은 디버깅과 조사를 해야 하다.

IBM® Rational® Purify®는 향상된 메모리 디버깅 도구로 잘못된 메모리 오류가 어디서 발생하는지 빠르고 정확하게 알아낼 수 있다. 이 도구를 사용하려면 먼저 Purity를 프로그램에 설치해야 한다. 그리고 나서 프로그램을 실행할 때 Purity가 프로그램에 의한 메모리 접근 및 조작을 세밀히 조사하고 발생하려는 메모리 오류를 식별한다. 이를 통해 디버깅 시간과 복잡성을 확연히 줄일 수 있다.

IBM developerWorks의 "Navigating "C" in a "leaky" boat? Try Purify라는 글을 읽으면 다양한 메모리 오류 유형과 Purify를 사용해 메모리 오류를 찾아내는 방법을 배울 수 있다. Purify와 메모리 오류에 익숙하다면 이 글을 읽을 필요가 없다.

Purify는 메모리 오류를 디버깅하는데 도움이 되는 몇 가지 독자적이고 향상된 기능을 가지고 있다. 이 글에서 먼저 API(Application Programming Interfaces)에 대해 소개하고 이를 디버거에서 어떻게 사용하는지 다룰 것이다. 그리고 나서 구체적으로 API에서 메모리 감시점(watch point)까지 다룰 것이다.

API(Application Programming Interface)

Purify는 프로그램이나 디버거에서 호출하여 메모리 문제를 디버깅하는데 도움이 되는 다양한 API를 제공한다. 예를 들어 purify_what_colors 함수를 사용해 메모리 범위의 상태를 찾을 수 있다.

int purify_what_colors (char *addr, unsigned int size);

Purify는 프로그램이 사용하는 메모리의 모든 바이트 상태를 추적하고 빨간색, 노란색, 초록색, 파란색의 네 가지 색깔을 사용해 상태를 표시한다. 처음에 모든 메모리는 메모리가 할당됐으나 초기화되지 않은 상태를 나타내는 빨간색이다. 메모리를 할당하고 나면 메모리가 할당됐으나 초기화되지 않은 상태를 나타내는 노란색이 된다. 메모리 위치를 초기화하면 메모리가 할당되고 초기화됐음을 표시하는 초록색이 되고, 메모리를 제거하면 전에 할당됐으나 이젠 제거된 메모리를 나타내는 파란색이 된다. 생명 주기는 그림 1에서 볼 수 있다.

  • 초록색으로 표시된 메모리는 읽거나 쓸 수 있다.
  • 노란색으로 표시된 메모리는 쓸 수는 있지만 읽을 순 없다. 읽게 되면 Purify는 UMR(초기화되지 않은 메모리 읽기) 오류를 보고한다.
  • 파란색이나 빨간색 상태의 메모리는 읽거나 쓸 수 없다.

잘못된 메모리를 디버그하며 디버거에서 purify_what_colors API를 호출하여 원하는 메모리 위치의 Purify 색깔 상태를 볼 수 있다. 이 예는 첫 두 바이트만 초기화된 8바이트 버퍼의 바이트 색깔 상태를 보여준다.

(gdb) print purify_what_colors(buf, sizeof(buf)+1) 
color codes of 9 bytes at 0xffffe820: GGYYYYYYR

API는 메모리 주소 buf에서 시작하는 sizeof(buf)+1 bytes의 메모리 상태를 출력한다. 메모리의 각 바이트 상태는 R, Y, G, B 중 한 문자로 나타난다. 이들 문자는 각각 빨간색, 노란색, 초록색, 파란색을 뜻한다.


그림 1. 메모리 위치의 생명 주기
메모리 위치의 생명 주기

Rational Purify를 디버거로 사용하기

대체로 Purify는 메모리 오류가 생겼을 때 원인을 찾고 수정할 수 있도록 충분한 정보를 제공한다. 하지만 어떤 경우 이 정보를 시작점으로 사용해 프로그램을 디버깅하여 이유를 찾아야 할 때도 있다. 예를 들어 Purify가 UMR 오류를 보고했을 때 프로그램을 초기화한다는 보고를 봐서 놀랐다고 하자. 분명 여기에는 초기화 보고가 실행되지 않았거나 메모리 버퍼의 포인터에서 포인터가 다른 메모리 버퍼에 재부여되어 초기화되지 않은 제어 경로가 존재한다.

이런 경우 프로그램을 디버깅하여 정확한 이유를 찾아야 한다. 좋은 점은 프로그램을 디버깅하는 대신 Purify가 설치된 프로그램을 디버그할 수 있다는 것이다. Purify는 오류가 발생하기 바로 전에 메모리 오류를 보고한다. 그러므로 디버거에서 관련 변수와 메모리 콘텐츠를 조사 및 분석할 수 있다. Purify는 또한 메모리 위치 상태를 조사하는 API를 제공한다.

Purify가 설치된 프로그램의 디버거를 사용하는 두 가지 방법이 있다.

  • 먼저 디버거 아래에 설치된 프로그램을 시작하고 Purify API 함수 purify_stop_here에 중지점을 놓는다. 디버거는 Purify 오류 메시지마다 멈출 것이다.
    (gdb) break purify_stop_here
    (dbx) stop in purify_stop_here
    (xdb) b purify_stop_here
    

  • 또는 Purify GUI의 Options > JIT Debug 메뉴를 통해 JIT(Just-In-Time, 적시) 디버깅을 구성해 원하는 오류 유형을 선택할 수 있다(그림 2 참조). 선택된 유형의 Purify 오류가 보고될 때마다 Purify는 디버거를 호출하고 이를 동작하는 애플리케이션에 붙인다.

그림 2. Purify JIT 디버거 대화상자
Purify JIT 디버거 대화상자

디버거 안에 있을 때 여러 가지 Purify API 함수를 사용해 다양한 메모리 위치의 상태와 유형을 조사할 수 있다.

  • purify_what_colors(char *addr, unsigned int size):
    앞의 API(Application Programming Interface) 부분에서 설명했듯이 메모리 주소(addr)에서 시작하는 size 바이트의 메모리 상태를 출력한다.
  • purify_describe(void *addr):
    addr 위치의 메모리에 대한 특정 상세 정보를 출력한다. 여기에는 위치(스택, 힙, 텍스트)가 포함되고 힙 메모리의 경우 할당된 체인을 호출하고 히스토리를 제거한다.
  • purify_assert_is_readable(const char *addr, int size):
    addr 주소에서 시작하는 size 바이트 읽기를 시뮬레이트하고 읽기가 초래하는 Purify 오류를 생성하고 오류에 따라 purify_stop_here를 호출한다. 오류가 잡히면 0을, 오류가 없다면 1을 반환한다.
  • purify_assert_is_writable(const char *addr, int size):
    addr 주소에서 시작하는 size 바이트 작성을 시뮬레이트하고 작성이 초래하는 Purify 오류를 생성하고 오류에 따라 purify_stop_here를 호출한다. 오류가 잡히면 0을, 오류가 잡히지 않으면 1을 반환한다.

디버깅하는 동안 몇 가지 코드에 집중하려고 할 것이므로 프로그램 제어가 코드에 닿기 전에 보고되는 Purify 오류에 관심을 갖지 말자. Purify는 API를 제공해 오류 보고를 끄거나 켠다(모니터링을 사용하는 메모리는 꺼지지 않는다).

  1. 디버거를 사용해 함수에 중지점을 놓고(또는 Purify 오류 보고를 끄려는 프로그램 위치에서) 설치된 프로그램을 작동한다.
  2. 디버거가 중지점에서 멈추면 다음 명령어를 입력한다.
    (gdb) print purify_stop_checking()
    

  3. Purify 오류 보고를 다시 시작하고자 하는 프로그램 위치에 중지점을 놓고 프로그램을 계속 작동한다.
  4. 디버거가 중지점에서 멈추면 다음 명령어를 입력한다.
    (gdb) print purify_start_checking()
    

메모리 감시점

Purify는 프로그램에서 잘못된 메모리 문제를 디버깅하는 데 도움이 되는 디버거에서 호출할 수 있는 다양한 메모리 감시점 API 세트를 제공한다. Listing 1의 코드는 메모리 누수와 동강난 포인터 모두를 보여준다.


Listing 1. 메모리 누수와 동강난 포인터 코드(mem_errors.c)
                
 1  #include <stdio.h>
 2  
 3  char *namestr;
 4  
 5  void foo() {
 6      namestr = (char *) strdup("Rational PurifyPlus");
 7      printf("Product = %s\n", namestr);
 8      free(namestr); /* free the memory allocated by strdup */
 9  }
10  
11  void main() {
12      namestr = (char *)malloc(20 * sizeof(char));
13      foo();
14      strcpy(namestr, "IBM");
15      printf("Company = %s\n", namestr);
16      free(namestr);
17  }

흥미롭게도 main() 메서드나 foo() 메서드를 독립적으로 본다면 두 함수 모두 문제가 없어 보인다. main()메서드는 메모리를 할당하고 foo()를 호출하고 할당된 함수를 사용한 후 이를 제거하고 나간다. foo() 메서드는 메모리를 할당하는 strdup() 메서드를 호출하고 메모리를 사용한 후 이를 제거한다. 하지만 이는 두 함수의 인터랙션이자 누수와 동강난 포인터 모두의 원인인 namestr라는 글로벌 포인터 변수를 사용한다. strdup()foo()에서 호출될 때 namestr 변수 값이 겹쳐서 써지므로 main()에 할당된 메모리의 포인터를 잃게 된다. 이는 곧 누수를 일으킨다. main에서 namestrfoo()에서 반환된 후 실제로 동강난 포인터인데, 메모리가 반환되기 전 foo()가 제거됐기 때문이다.

이처럼 간단한 예에서 코드를 조사해 문제를 찾기는 쉽다. 하지만 문제를 가진 함수가 각기 다른 라이브러리에 있을 수 있는 복잡한 제어 흐름과 큰 프로그램에서는 이것이 불가능하다. 이 때 Purify와 Purify의 메모리 감시점을 유용하게 사용할 수 있다.

이 프로그램을 정화할 수 있는 방법은 다음과 같다.

$ purify cc -g mem_errors.c -o mem_errors.pure

정화된 프로그램을 작동할 때 Purify는 다음과 같은 오류를 보고할 것이다(그림 3 참조).

  • main 함수에서 namestr로 할당된 메모리를 위한 MLK (Memory Leak)
  • main 함수에서 strcpy() 호출에 FMW (Free Memory Write)
  • main 함수에서 printf() 호출에 FMR (Free Memory Read)
  • main 함수에서 free() 호출에 FUM (Freeing Unallocated Memory)

그림 3. mem_errors.c에서 Purify에 의해 보고된 메모리 오류
mem_errors.c에서 Purify에 의해 보고된 메모리 오류

이처럼 작은 예제에서 프로그램 상의 실수는 Purify 오류와 함께 제공된 정보를 사용해 쉽게 수정할 수 있다. 그러나 다양한 위치 및 루프 등에서 호출돼는 foo() 같은 함수가 있는 복잡한 프로그램의 경우 Purify 메모리 감시점 API를 사용해 디버깅하는 것이 매우 유용하다.

감시점 기능 덕분에 Purify가 메모리 분야와 메모리를 읽거나(WPR: 감시점 읽기) 쓰거나(WPW: 감시점 작성하기) 제거할 때(WPF: 감시점 제거하기)의 보고에 집중할 수 있다. 이 경우 "프로그램 어디서 이 변수가 작성됐나?", "이 변수가 사용된 곳은 어디인가?" 혹은 힙의 메모리에서 "이 메모리를 제거한 함수는 무엇인가?"와 같은 물음에 대답할 수 있다.

다음은 프로그램을 정화하고 디버거 아래에서 작동하는 방법이다(여기서 gdb는 프로세스를 설명하는데 사용됐지만 원하는 어떤 디버거나 사용할 수 있다).

$ purify cc -g mem_errors.c -o mem_errors.pure
$ gdb mem_errors.pure

Purify가 메모리 누수와 FMR, FMW, FUM을 보고하는데 몇 가지 의문점이 생길 것이다.

  • namestr 변수가 main()에 할당된 메모리의 포인터라면 왜 namestr는 다른 메모리를 가리키는가?
  • namestr는 정확히 어디서 다른 주소 값과 함께 작성되고 이에 따라 누수의 원인이 되는 main()에 할당된 메모리의 마지막 포인터를 잃는가?
  • main()main()에 메모리가 할당된 후 namestr 값은 어디서 사용되고 겹쳐 작성되는가?

이 질문들은 main()함수(13행)의 malloc 호출 바로 다음에 중지점을 놓음으로써 대답할 수 있고, Purify를 사용해 메모리 감시점을 &namestr 에 설정함으로써 namestr 값에서 작성된 모든 운영을 추적한다. namestr 값에 주소가 작성될 때마다 Purify 감시점은 WPW(감시점 작성하기) 메시지를 Purify 뷰어에 보여줄 것이다.

$ gdb mem_errors.pure
(gdb) break 13
Breakpoint 1 at 0x10000aec: file mem_errors.c, line 13.
(gdb) run
Starting program: mem_errors.pure 
Breakpoint 1, main () at mem_errors.c:13
13          foo();
(gdb) print purify_watch_n(&namestr, 4, "w")
$1 = 1
(gdb) continue

purify_watch_n() 함수는 조사해야 할(&namestr) 메모리 위치의 주소와 크기(포인터 크기는 4바이트), 감시 모드(읽기에 r, 작성에 w, 읽기-작성에 rw)를 갖는다. 새 주소가 namestr에 저장될 때마다 Purify는 뷰어에 WPW(감시점 작성하기)를 결과로 보여줄 것이다. 즉, 다음과 같은 메시지로 나타날 것이다.

WPW: Watch point write:
  * This is occurring while in:
        foo            [mem_errors.c:6]
        main           [mem_errors.c:13]
        __start        [mem_errors.pure]
  * Watchpoint 1
  * Writing 4 bytes to 0x20103b38 in the initialized data section.
  * Value changing from  537934728 (0x20103b88, " \020;\210")
                     to  537934968 (0x20103c78, " \020")
  * Address 0x20103b38 is global variable "namestr".
    This is defined in mem_errors.pure.

이 메시지는 6행의 foo() 메서드에서 다른 주소가 namestr에 저장됨을 뜻한다. 메모리를 제거하기 전에 이미 namestr 값이 변경됐으므로 Purify는 MLK(메모리 누수) 오류를 보고한다. 이제 FMR(메모리 읽기 제거)과 FMW(메모리 쓰기 제거) 오류의 원인을 디버깅해 보자. FRM이나 FMW가 보고될 때 Purify 또한 메모리 할당이 어디에서 이루어지는지 지정한다. 예를 들어 Purify는 14행과 15행의 FMW와 FMR 오류가 각각 main() 메서드에서 발생함을 나타내는데, foo() 메서드의 6행에서 strdup() 호출에 의해 할당되어 이미 제거된 메모리에 접근할 수 있기 때문이다. 그러므로 6행에 할당된 메모리 블럭(포인터가 아닌) 상의 모든 읽기와 쓰기를 추적해야 한다. strdup() 호출 후 읽기-쓰기 감시점을 두어 이를 실행할 수 있다.

(gdb) break 7
Breakpoint 2 at 0x10000c38: file mem_errors.c, line 7.
(gdb) continue
Continuing.
Breakpoint 2, foo () at mem_errors.c:7
7           printf("Product = %s\n", namestr);
(gdb) print purify_watch_n(namestr, 20, "rw")
$2 = 2

이것이 읽기-쓰기 감시점이기 때문에 namestr가 가리키는 메모리 블럭의 콘텐츠를 읽거나 수정하려는 시도는 각각 WPR이나 WPW 메시지를 발생시킨다. 여기서 namestr를 사용하는 것과 이전에 &namestr 를 사용하는 것에는 차이가 있다. 이전 예는 namestr 포인터 자체를 갖는 메모리를 살폈기 때문에 관련 영역의 주소는 &namestr 에 의해 주어졌다. 대신 두 번째 예는 namestr가 가리키는 메모리를 살핀다.

WPR은 printf() 호출의 7행에서, WPF(감시점 제거)는 free() 호출의 8행에서 나타난다. 이 모두를 예상했지만 이제 WPF가 보고된 후에는 더 조심스럽게 제어 경로를 따라야 한다.

사실 Purify가 모든 오류(혹은 메시지)에 멈추도록 purify_stop_here(앞서 "디버거로 Purify 사용하기" 절에서 설명했던 것처럼)에 중지점을 설정할 수 있다. 이 메모리로의 모든 접근은 오류가 발생해야 한다. 메모리를 가리키는 namestr가 제거됐기 때문이다. 그러므로 14행의 코드를 따르면 WPW 메시지를 생성한다. 이미 제거된 메모리가 strcpy()에 호출됨으로써 메시지에 작성되기 때문이다. 이 WPW는 Purify가 보고한 FMW(메모리 작성 제거)를 설명한다.

(gdb) next
main () at mem_errors.c:14
14          strcpy(namestr, "IBM");
(gdb) next
15         printf("Company = %s\n", namestr);

이와 비슷하게 15행을 따르면 WPR 메시지를 생성하므로 FMR 오류가 된다. 16행의 프로그램 마지막으로 보고되는 FUM 오류 또한 이제 분명하다. namestr(strdup에 의해 할당된 새 값) 메모리가 이미 8행의 foo 함수(WPF 메시지가 보고된)에서 제거됐기 때문이다.

그림 4는 샘플 Purify 창과 모든 감시점 오류를 보여준다. 요약하면 메모리 감시점은 주어진 메모리 블럭을 추적하는데 도움이 된다. 디버거와 함께 쓰면 메모리 오류를 조작하는 프로그램을 실행하고 사용하는 메모리를 추적하는데 도움이 된다.


그림 4. Purify에 의해 보고된 감시점 메시지
Purify에 의해 보고된 감시점 메시지

Purify 감시점은 주어진 메모리 주소를 위해 다음과 같은 메시지를 보고할 수 있다.

  • 읽기
  • 쓰기
  • 할당
  • 할당 제거
  • entry 함수 범위에 들어오기
  • exit 함수 범위 밖으로 나가기

표 1에 몇 가지 감시점 API 함수를 정리했다. 가장 간단한 것은 purify_watch(addr)로 읽기-쓰기 감시점을 주어진 주소에서 시작하는 4바이트에 설정한 것이다. 감시점을 설정한 API는 막 설정된 감시점 수인 정수 값을 반환한다. 이 정수를 watchpoint_remove로 보내 제거할 수 있다. 모든 편리한 API 함수는 purify_watch_n 을 적절한 주소, 크기, 유형으로 사용하는 것과 같다.


표 1. 감시점을 설정하는 API
감시점 API설명
purify_watch_n(addr, size, type) addr에서 시작하는 size 바이트에 type 유형의 감시점을 설정한다. Type으로 읽기("r"), 쓰기("w"), 읽고 쓰기("rw")를 설정한다.
purify_watch(addr)
purify_watch_1(addr)
purify_watch_2(addr)
purify_watch_4(addr)
purify_watch_8(addr)
addr, ("rw") 유형에서 시작하는 4바이트(또는 나타낸 수)
purify_watch_r(addr)
purify_watch_r_1(addr)
purify_watch_r_2(addr)
purify_watch_r_4(addr)
purify_watch_r_8(addr)
addr, ("r") 유형에서 시작하는 4바이트(또는 나타낸 수) 관찰
purify_watch_w(addr)
purify_watch_w_1(addr)
purify_watch_w_2(addr)
purify_watch_w_4(addr)
purify_watch_w_8(addr)
addr, ("w") 유형에서 시작하는 4바이트(또는 나타낸 수) 관찰
purify_watch_rw(addr)
purify_watch_rw_1(addr)
purify_watch_rw_2(addr)
purify_watch_rw_4(addr)
purify_watch_rw_8(addr)
addr, ("rw") 유형에서 시작하는 4바이트(또는 나타낸 수) 관찰

감시점과 이들 제거에 관한 정보를 얻으려면 다음 API를 사용한다.

  • Purify 메모리 감시점의 모든 활동을 보여주는 purify_watch_info()
  • 주어진 수로 감시점을 제거하는 purify_watch_remove(int watchpoint_no)
  • 모든 감시점을 제거하는 purify_watch_remove_all()

프로그램에서 Purify API 사용하기

디버거에서 Purify API를 사용하는 것에서 벗어나 오류 추적과 추가 정보 보고를 위해 프로그램에서 이들을 끼워넣을 수도 있다. 이런 경우 오류가 발생했을 때 자동화된 테스트 스위트를 통해 정화된 프로그램을 작동하더라도 문제 식별에 도움이 되도록 추가 메시지를 Purify 로그에 복사할 것이다.

프로그램에 정화된 API를 끼워넣는 두 가지 방법이 있다.

  • #ifdef 가드 사용하기
  • Purify 스터브에 연결하기

가드 사용하기

Listing 2의 예제처럼 #ifdef 정의 가드를 둘러싸 프로그램의 Purify API 호출을 보호할 수 있다. 이 경우 Purify API를 활용하는 정화된 실행 가능 프로그램을 만들거나 제품으로 보낼 실행 가능한 프로그램을 만들기 위해 소스 코드를 바꿀 필요가 없다.

예제에는 소스와 결과 열이 각각 읽고 쓸 수 있는지 먼저 확인하는 곳인 strncpy 구현이 있다. 어떤 테스트라도 실패한다면 purify_printf를 호출하여 적절한 메시지를 Purify 콘솔이나 로그에 출력한다. 그리고 나면 메모리 위치(스택, 힙, 텍스트)를 포함하는 메모리 주소에 관한 특정 디테일과 힙 메모리, 할당 시간과 free() 호출 히스토리의 호출 체인을 출력하는 purify_describe가 호출된다. 마지막으로 purify_what_colors를 호출하여 메모리 버퍼의 색깔을 출력한다. 복사본은 오류가 없을 때만 수행된다.


Listing 2. Purify API를 가드로 사용하는 mystring.c 파일의 부분
                
#ifdef PURIFY
#include <purify.h>
/*
 * The purify.h file has needed API declaration.
 */
#endif

void mystrncpy(char* dest, const char* src, int length) {
#ifdef PURIFY
    if (!purify_assert_is_readable(src, length)) {
        purify_printf("strncat: caller gave bad source");
        purify_describe(src);
        purify_what_colors(src, length);
    } else if (!purify_assert_is_writable(dest, length)) {
        purify_printf("strncat: caller gave bad destination");
        purify_describe(dest);
        purify_what_colors(dest, length);
    } else {
#endif
        /*
         * skip: copy n bytes from src to dest only if safe
         */
#ifdef PURIFY
    }
#endif
}

int main() {
    /* skip: main body that calls mystrncpy */
}


Listing 3makefile은 실행 가능한 파일(mystring.pure의 규칙 참조)의 정화된 버전을 만들고 이를 Purify API 라이브러리와 연결하는데 -DPURIFY 플래그를 사용하여 Purify API 호출을 어떻게 켤 수 있는지 보여준다.


Listing 3. mystringmystring.pure를 만드는 makefile의 부분
                
#
# makefile to build mystring programs, and its purified versions
#

# ... skip ...

# Purify header and API lib locations
PURIFYINCLUDE = -I`purify -print-home-dir`
# For 64-bit program, replace lib32 by lib64 in following:
PURIFYAPILIB  = `purify -print-home-dir`/lib32/libpurify_stubs.a 

# ... skip ...

mystring : mystring.c
	$(CC) $(FLAGS) -o $@ $?

mystring.pure : mystring.c
	purify $(CC) $(FLAGS) -g -DPURIFY $(PURIFYINCLUDE) -o $@ $? \
		$(PURIFYAPILIB)

# ... skip ...


Purify 스터브에 연결하기

가드 사용의 단점은 전체 프로그램을 재컴파일해야 한다는 점이다. 이 예제에서는 단 하나의 C 파일만 사용됐지만 큰 시스템에서는 전형적으로 다양한 라이브러리가 만들어지고 마지막으로 실행 가능한 파일을 만드는데 연결된다. 이런 상황에서 프로그램을 정화하고자 한다면 가드를 사용하는 모든 C 파일을 재컴파일해야 한다.

또는 Listing 3의 규칙을 변경하여 애플리케이션을 항상 libpurify_stubs.a와 연결해 mystring을 만들면 된다.

mystring : mystring.c
	$(CC) $(FLAGS) -o $@ $? $(PURIFYAPILIB)
            

libpurify_stubs.a는 모든 Purify API 함수에 빈 스터브를 갖는 작은 라이브러리다. 프로그램에 설치할 때 Purify는 API 함수의 실제 정의를 제공하고 스터브는 무시된다.

설치되지 않은 프로그램(Listing 4 참조)의 속도가 떨어지는 것에서 Purify 함수 호출을 지키기 위해 다중 Purify API를 if(purify_is_running())로 둘러쌀 수 있다.


Listing 4. 가드 없이 Purify API를 사용하는 mystring.c 파일의 부분
                
#include <purify.h>
/*
 * The purify.h file has needed API declaration.
 */

void mystrncpy(char* dest, const char* src, int length) {
if (purify_is_running()) {
        if (!purify_assert_is_readable(src, length)) {
            purify_printf("strncat: caller gave bad source"); 
            purify_describe(src);
            purify_what_colors(src, length);
        } else if (!purify_assert_is_writable(dest, length)) {
            purify_printf("strncat: caller gave bad destination"); 
            purify_describe(dest);
            purify_what_colors(dest, length);
        }
}

    /*
     * skip: copy n bytes from src to dest only if safe
     */
}

int main() {
    /* skip: main body that calls mystrncpy */
}



#ifdef와 Purify 스터브를 사용하는 것의 차이는 전자의 경우 플래그로 프로그램을 재컴파일해야 하고, 후자는 purify_is_running(무시해도 됨)을 호출하는 런타임 비용과 관련되어 생산 코드에서마저 프로그램을 Purify의 빈 스터브와 연결한다는 것이다. 그러니 필요에 따라 맞는 방법을 사용하기 바란다.

요약

이 글에서는 Purify, API, 메모리 감시점에서의 메모리 색깔 개념에 대해 배웠다. 디버거에서 이들 API를 사용하거나 대신 이를 프로그램에 끼워넣을 수 있다. 어떤 방법이든 Purify API와 메모리 감시점의 협조로 프로그램의 메모리 오류를 보다 효과적으로 디버깅할 수 있다.

Share this...

digg Digg this story
del.icio.us Post to del.icio.us
Slashdot Slashdot it!



참고자료

교육

제품 및 기술 얻기

토론


필자소개

Satish Chandra Gupta는 인도 방갈로르에 위치한 IBM Rational® PurifyPlus® 그룹의 개발자다. 관심 분야는 컴파일러, 프로그래밍 언어, 런타임 분석, 자바 메모리 누수, 유형 이론, 소프트웨어 엔지니어, 소프트웨어 개발 환경 등이다. 이와 관련한 연구가 ACM/IEEE 컨퍼런스에 출간됐다. 인도 칸푸르의 Indian Institute of Technology에서 학사 학위를, 미국 밀워키의 University of Wisconsin에서 석사 학위를 받았다.


Anand Gaurav는 인도 벵갈루루에 위치한 IBM Rational PurifyPlus 그룹의 개발자다. 관심 분야는 런타임 분석, 객체 지향 디자인, 데이터 구조, 알고리즘 등이다. 인도 카르나타카에 위치한 PESIT(VTU)를 졸업했다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의