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

한국 developerWorks  >  Rational  >

IBM Rational Purify의 향상된 기능: Purify 도구와 보고 커스터마이징

Purify 옵션과 지시어를 사용해 애플리케이션에 맞게 커스터마이즈하기

developerWorks
문서 옵션

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

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

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

2008 년 5 월 06 일

IBM® Rational® Purify®는 분석과 수정이 매우 어려운 잘못된 메모리 오류를 정확히 찾아내는 도구입니다. 이 글에서는 Purify 옵션과 지시어를 사용해 애플리케이션의 필요에 맞게 Purify를 커스터마이즈하는 방법에 대해 설명하겠습니다.

IBM® Rational® Purify®는 메모리 오류를 찾는 향상된 도구로 잘못된 메모리 오류를 빠르고 정확하게 분리한다. Purify를 사용해 애플리케이션에 장착한 후 이 애플리케이션을 실행하면 Purify는 모든 메모리 접근을 세밀하게 조사해 오류가 발생하기 전에 악영향을 주는 오류를 보고한다. 다양한 메모리 오류의 유형과 Purify를 사용해 이를 찾아내는 방법은 이전 글인 Navigating "C" in a "leaky" boat? Try Purify에서 배울 수 있다.

메모리 오류 보고서를 만드는 것에는 복잡한 소프트웨어 개발 환경에서 Purify를 사용해 튜닝과 커스터마이즈를 가능케 하는 몇 가지 향상된 기술도 포함된다. 이 글에서는 먼저 Purify 옵션(options)지시어(directives)에 대한 개요를 설명한다. 그리고 나서 이를 사용해 Purify의 다음과 같은 부분을 커스터마이즈하는 방법에 대해 다룬다.

  • 캐시 관리: 설치된 바이너리 파일 관리, 공유
  • 부분적 Purify: 애플리케이션의 일부분만 설치해 실행 시간 오버헤드와 오류 찾기 범위 줄이기
  • 힙 메모리 관리: 실행 중인 설치된 애플리케이션에서 메모리 오버헤드를 제한하고 힙 사용 보고서 커스터마이즈하기

Purify 옵션

Purify는 정교한 제어가 가능한 다음 두 가지 유형의 풍부한 옵션 세트를 제공한다.

  • 빌드 타임(build-time) 옵션
  • 런타임 옵션

빌드 타임 옵션은 설치시에 사용해야 한다. 예를 들어 프로그램의 정적 데이터 상에서 Purify가 버퍼 오버런을 확인하지 않기를 원한다면 Purify를 장착하는 동안 -static-checking 옵션을 사용할 수 있다.

$ purify -static-checking=no cc your_prog.c

런타임 옵션은 이름에서 알 수 있는 것처럼 설치된 프로그램의 런타임에 영향을 준다. 예를 들어 오류가 보고될 때 Purify에 더 긴 스택 호출 체인을 보여달라고 명령할 수 있다.

$ purify -chain-length=10 cc your_prog.c

연결 라인에 깃발처럼 나타나는 것 외에도 빌드 타임과 런타임 옵션 모두 PUREOPTIONSPURIFYOPTIONS 환경 변수를 사용해 지정할 수 있다.

  • PUREOPTIONS 환경 변수는 Purify와 Quantify나 PureCoverage 같은 다른 PurifyPlus 제품에도 적용된다.
  • PURIFYOPTIONS 환경 변수는 Purify에만 적용된다.

앞서 언급한 옵션을 sh, kshbash 셸에서는 다음과 같이 설정하라.

$ export PURIFYOPTIONS="-static-checking=no -chain-length=10"

cshtcsh 셸에서는 다음 옵션을 사용한다.

% setenv PURIFYOPTIONS "-static-checking=no -chain-length=10"

빌드 타임 옵션에서 연결 라인에 지정된 값은 환경 변수에서 같은 옵션의 값을 오버라이드한다. Purify는 도구가 사용되는 동안(연결 라인이나 환경 변수를 통해) 지정된 런타임 옵션을 설치된 프로그램에 저장하고 프로그램이 작동할 때 이를 사용한다. 실행시 환경 변수에 지정된 런타임 옵션은 설치된 프로그램에 저장된 같은 옵션 값을 오버라이드한다( -ignore-runtime-environment 빌드 타임 옵션이 사용되지 않는 한).

또는 원한다면 Purify GUI를 통해 런타임 옵션을 지정할 수 있다. 이 경우 이 값들은 환경 변수와 연결 라인을 통해 지정된 같은 옵션 값을 오버라이드한다.

Purify는 몇 가지 빌드 타임 옵션을 제공해 Help와 버전 열에 접근한다.

$ purify -version
$ purify -usage
$ purify -help
$ purify -onlinehelp

또 다른 간편한 빌드 타임 옵션은 -print-home-dir 로 Purify가 설치된 디렉터리 이름을 출력한다. 이는 예를 들어 purify_what_options 스크립트를 실행해 도구 사용시 어떤 옵션이 사용됐는지 찾는 데 사용할 수 있다.

$ `purify -print-home-dir`/purify_what_options <your_prog.pure>

또 다른 흥미로운 사용법은 purify.h를 포함하는 프로그램을 컴파일해 Purify API에 접근할 때다. 헤더 파일이 Purify 홈 디렉터리에 나타나는데, 이 명령은 컴파일러의 include 파일 검색 경로의 디렉터리를 포함할 것이다.

$ cc -c -I`purify -print-home-dir` your_prog.c

Purify 지시어

명령행 옵션에 추가로 Purify를 통해 다양한 지시어를 지정하여 도구 사용 튜닝과 오류 보고를 찾을 수 있다. 지시어는 프로젝트에 맞게 Purify 사용을 커스터마이즈하는 데 도움이 된다. 예를 들어 프로젝트에 사용하는 써드파티 라이브러리(libfoo 같은)의 함수 중 하나(예를 들어 foo)에 메모리 오류(초기화되지 않은 메모리 읽기 또는 UMR이라고 하자)가 있다고 가정하자. 이 오류를 고칠 수 없고 벤더에 오류를 보고하고 수정을 기다리는 것 외에는 할 수 있는 게 없을지도 모른다. 고칠 수 없기에 Purify 보고에서 이 오류를 보고 싶지 않다면 suppress 지시어를 지정하면 된다.

suppress umr foo

이 지시어는 foo 함수에서 UMR을 보여주지 말라는 것이다. Purify GUI에서 suppress 지시어로 감춰진 오류를 보고 싶다면 View 메뉴에서 Suppressed를 클릭한다. Purify를 원하는 만큼 한정짓거나 일반화할 수 있다. 다음에 몇 가지 예가 있다.

  • 특정 호출 체인에서만 UMR을 비활성화하려면 전체 호출 체인을 제공할 수 있다.
    suppress umr printf; foo; bar; main
    

  • 부분적으로 지정된 호출 체인과 맞는 UMR과 호출 체인을 비활성화하려면 함수 이름의 규제 없이 생략(…)이 함수에 나타나는 수를 의미하는 곳이어야 한다.
    suppress umr ...; foo; ...; main 
    

  • (써드파티) 라이브러리에서 모든 UMR을 비활성화하려면 별표(*)가 문자에 나타나는 수를 의미하는 곳이어야 한다.
    suppress umr "libfoo*"
    

  • 어디든 나타나는(매우 위험하지만) 모든 UMR을 비활성화하려면 다음과 같다.
    suppress umr *
    

Purify는 이들 위치와 명령의 .purify라는 파일 이름에 지정된 지시어를 처리한다.

  1. <purify-installation-home>/.purify
  2. <users-home-dir>/.purify
  3. <current-working-directory>/.purify

커스터마이제이션이 모든 Purify 사용자를 위한 것인지 모든 프로젝트나 프로젝트 중 하나를 위한 것인지에 따라 이 파일들 중 하나를 편집하고 지시어를 추가할 수 있다.

또는 비활성화하려는 Purify GUI 내의 오류 중 하나를 선택하고 마우스 오른쪽 버튼을 클릭해 팝업 메뉴를 띄워 메뉴에서 Suppress를 선택할 수 있다. 이렇게 하면 그림 1처럼 대화상자가 나타날 것이고 여기에서 오류 유형과 호출 체인 등을 지정할 수 있다. 적절한 .purify 파일 위치를 지정하고 Make permanent 버튼을 클릭해 영구적으로 비활성화할 수 있다.


그림 1. Purify Suppression 대화상자
Purify Suppression 대화상자

또 다른 지시어인 kill은 메뉴를 사용해도 GUI에 메시지가 나타나지 않는다는 것을 제외하면 suppress와 똑같이 동작한다. 오류가 수없이 나타난다면(몇천 번, 몇만 번) 이를 비활성화하는 것보다는 없애야지 프로그램이 더 빨리 작동한다.

캐시 관리

프로그램을 설치할 때 Purify 또한 프로그램이 직간접적으로 의존하는 모든 라이브러리( libc 같은)를 설치하고 원래 라이브러리 대신 이렇게 설치된 라이브러리와 프로그램을 묶는다. 라이브러리를 포함한 디렉터리가 쓰기 가능하다면 Purify는 같은 디렉터리에 라이브러리가 설치된 버전을 만든다. 그렇지 않다면 Purify 설치 영역의 기본 캐시 디렉터리에 이를 만든다.

캐시와 관련된 다양한 빌드 타임 옵션이 있다. 예를 들어 원래의 라이브러리가 나타나는 다양한 디렉터리에 설치된 라이브러리를 버리고 싶지 않고, 대신 장착된 라이브러리를 캐시 디렉터리에 저장하고 싶다면 -always-use-cache-dir 옵션을 사용할 수 있다. 또한 -cache-dir=<dir-name> 옵션을 통해 기본 디렉터리를 사용하는 대신 선택한 캐시 디렉터리를 지정할 수 있다. 이들 옵션을 사용해 지정한 캐시 디렉터리를 간단히 제거함으로써 모든 설치된 라이브러리를 캐시에서 제거할 수 있다.

프로그램을 설치할 때 Purify는 마지막으로 사용된 이후 수정된 적이 있거나 라이브러리 설치 버전을 찾을 수 없을 때만 라이브러리를 설치한다. 이렇게 하면 설치 시간을 줄일 수 있다. 라이브러리의 이전 설치 버전을 없애려면 -force-rebuild 빌드 타임 옵션을 사용하면 Purify가 모든 필요한 라이브러리를 재설치할 것이다. 향상된 설치-타임 옵션을 사용할 때( -static-checking-guardzone 같은)와 재설치할 때 도움이 된다.

일반적으로 한 머신에 설치된 프로그램을 다른 머신에서 실행할 수는 없다. 이유는 간단하다. 보통 프로그램은 libc 같은 시스템 라이브러리에 의존하는데 이 라이브러리들은 다양한 운영체제 패키지의 패치 수준을 기반으로 머신에 따라 달라질 수 있다. 각 프로그램이 같은 시스템 상의 원래 라이브러리와 같은 설치된 라이브러리를 가지려면 Purify의 캐싱 메커니즘은 호스트 이름에 따라 설치된 라이브러리를 정리해야 한다. 또한 프로그램은 모든 머신 상의, 또는 적어도 같은 네트워크 위치에서 접근하는 같은 라이브러리나 써드파티 라이브러리에 의존한다. 이런 환경에서 캐시가 각 호스트 이름에 다중 설치된 버전의 라이브러리를 갖는 걸 원하지 않을 것이다. 이들 복사본은 똑같을 것이고 저장소 용량을 낭비하기 때문이다. IBM® AIX®, 리눅스(Linux®), 솔라리스(Solaris®), 유닉스(UNIX®) 플랫폼에서 Purify의 repure 메커니즘을 사용해 설치 시 몇 가지 문제를 해결함으로써 이를 피할 수 있다.

  1. 먼저 모든 머신에 존재하는 경로를 택하되 실제 저장소의 경로는 각 머신에 로컬이어야 한다. 예를 들어 /tmp 디렉터리는 모든 유닉스(UNIX®) 머신에 존재하지만 이는 각 머신에 로컬이고 두 머신이 공유하진 않는다. : 홈 디렉터리나 NIS(네트워크 정보 서비스)나 NFS(네트워크 파일 시스템)로 다른 머신에 보이는 위치의 경로는 사용하지 말자.
  2. -local-cache-dir 옵션을 사용해 다음과 같은 경로에서 시스템에 제한적으로 설치된 라이브러리를 저장하도록 Purify에 지시한다.
    $ purify -always-use-cache-dir -local-cache-dir=/tmp -cache-dir=./cache \
          cc -g test.c -o test.pure
    

  3. 같은 머신에서 설치된 프로그램을 실행할 수 있다.
    $ ./test.pure
    

  4. 다른 머신에서 설치된 프로그램을 실행하려면 repure를 사용해 다른 머신에서 프로그램을 "재정화(repurify)"하기만 하면 된다.
    $ repure ./test.pure
    

  5. repure를 사용한 후 다른 머신에서도 프로그램을 실행할 수 있다.
    $ ./test.pure
    

  6. 설치된 프로그램을 실행하고자 하는 각 머신에 repure 단계(4단계)를 반복한다.

Purify 설치의 홈 디렉터리에 위치한 pure_remove_old_files 스크립트를 사용해 예전에 설치된 파일을 삭제할 수 있다.

`purify -print-home-dir`/pure_remove_old_files <path> <days>

이 명령은 설치된 라이브러리 복사본( _pure_ 가 들어간 파일 이름과 끝에 .so .sl 이 들어간 공유된 라이브러리 확장자를 가진 파일 이름)만을 제거한다. 예를 들어 14일 이상된 모든 설치 파일과 파일 시스템 어딘가에 저장된 모든 파일을 다음을 통해 삭제할 수 있다.

$ pure_remove_old_files / 14

부분적 Purify

Purify는 원하는 위치에 코드를 설치하고 새로운 지시를 삽입함으로써 유효하지 않은 메모리 접근을 확인한다. 이 때 필요한 데이터를 확인, 유지하는 것은 수행 오버헤드에 결과로 나온다. 가끔 애플리케이션의 선택된 컴포넌트에서만 메모리 오류를 찾고 싶을 때가 있다. 예를 들어 애플리케이션이 사용하는 써드파티와 공유된 라이브러리의 메모리 오류에만 관심이 있다고 하자. 이 경우 애플리케이션에 선택적으로 설치하고 관심 밖의 공유 라이브러리는 제외하면 된다. 이 메커니즘을 통해 큰 애플리케이션에서도 코드의 주요 컴포넌트에만 설치하면서 설치된 애플리케이션이 실행되는 데 드는 오버헤드 시간을 줄일 수 있다.

주의:
선택적 설치는 HP-UX와 AIX 플랫폼에서만 가능하다. 솔라리스 SPARC에서는 지원이 제한되고 솔라리스 x86과 리눅스에서는 전혀 지원되지 않는다.

공유된 라이브러리를 제거할 때 여전히 Purify는 제거된 공유 라이브러리에 할당된 메모리에서 메모리 누수와 같은 힙 관리 오류를 찾는다.

selective 도구를 사용해 라이브러리를 제거하는 방법은 다음과 같다.

$ purify -selective -exclude-libs=libfoo1.so:libfoo2.so cc -g app.c \
      -o a.out.pure -lfoo1 -lfoo2 -lbar

-exclude-libs 옵션을 사용해 배제하고 싶은 라이브러리를 콜론으로 분리된 목록으로 제공할 수 있다. 또는 .purify 지시어 파일의 exclude 지시어를 사용할 수도 있다.

exclude libfoo*

앞에서 언급했듯 지시어는, 별표(*)를 나타난 문자의 수로 해석한다.

-selective 옵션은 Purify가 몇몇 라이브러리를 배제하면서 생길 수 있는 가짜 오류를 잡아내고 제거하는 더 강력한 알고리즘을 사용하게 하는 이유가 된다. 다음 예를 보자.

  • main()foo()를 호출하고 foo()bar()를 호출한다고 하자. 그리고 설치 시에 foo()를 배제한다고 하자. 또한 main()은 메모리를 할당하고 foo()는 이 메모리를 초기화하며 bar()는 이 메모리를 사용한다고 가정하자. foo()가 설치되지 않았다면 Purify는 메모리가 foo()에서 초기화됐음을 모르고 메모리가 bar()에서 사용될 때 초기화되지 않은 메모리 읽기(UMR) 오류를 보고할 것이다. -selective 옵션은 이런 가짜 오류를 제거하도록 Purify에 명령할 것이다.
  • 라이브러리를 배제하면 설치된 코드에서조차 Purify가 놓치는 몇 가지 오류가 생길 것이다. main()foo()를 호출하고 초기화된 버퍼를 보낸다고 가정하자. Purify는 함수 foo()의 버퍼를 사용할 때마다 UMR을 보고할 것이다. foo()가 공유된 라이브러리 libfoo.so에 있고 이를 배제한다고 하자. 이 경우 foo()가 설치되지 않았으므로 Purify가 삽입한 메모리 확인 코드가 없을 것이고 설치된 코드에 오류가 있음에도 불구하고 오류가 없다고 보고할 것이다. 설치된 함수 main()이 초기화되지 않은 버퍼를 foo()에 잘못 전달했기 때문이다.

위의 두 가지 예는 설치된 버전에서 실행되는 실행시간 오버헤드를 줄이는 것과 오류 찾기의 광범위함 사이에서 절충(tradeoff)을 강조한다. 그러므로 애플리케이션에서 라이브러리의 역할과 메모리 관련 인터랙션의 특징을 자세히 조사해 어떤 공유된 라이브러리를 배제할 것인지 신중히 판단해야 한다.

Purify는 오류가 나타나기 전이나 버퍼가 제거되더라도 설치된 코드의 버퍼 오버런 오류를 찾을 수 있다. Purify는 오류가 나타날 때 오류를 보고하지만 설치되지 않은 코드를 확인할 기회가 없으므로 설치되지 않은 코드에서 발생하는 오류는 보고할 수 없다. -late-detect-logic 옵션을 사용한다면 Purify는 힙 메모리 블록이 제거될 때 추가로 확인할 수 있다. 버퍼 오버런을 찾는다면 ABWL(Array Bound Write Late) 오류를 보고할 것이다.

힙 메모리 관리 오류

Purify는 메모리와 설치된 프로그램 실행 시에 낭비되는 시간을 제어하는 몇 가지 옵션을 제공한다. 프로그램에서 메모리 블록을 제거할 때 Purify는 이 메모리를 즉시 제거하지 않는다. 대신 제거된 블록을 FIFO 큐(First In-First Out queue)에 추가한다. 이렇게 되면 Purify는 메모리가 제거된 후 프로그램이 이 메모리에 접근하는 현수 포인터(dangling pointer)를 찾고 보고한다(제거된 메모리 읽기/쓰기 또는 FMR/FMW 오류). Purify가 힙에서 블록을 제거하도록 한다면 메모리는 바로 재사용되고 Purify는 예전 블록의 유효하지 않은 접근과 새 블록의 유효한 접근 사이의 차이를 인식할 수 없게 된다.

큐 길이의 기본 값은 100이다. 큐가 꽉 차면 Purify는 큐의 첫 번째 블록을 제거하고 새로 제거된 블록을 여기에 추가한다. 큐의 길이는 -free-queue-length=<value> 옵션을 사용해 바꿀 수 있다. 더 긴 큐 길이는 시간을 확장해 현수 포인터가 제거된 메모리를 신뢰성 있게 찾고 보고할 수 있을 것이다. 하지만 제거된 큐 길이가 길어지면 더 많은 메모리(실제든 가상이든)를 사용하게 된다. 제거된 메모리 동작이 지연되기 때문이다. 그러므로 큐의 길이가 길어지면 현수 포인터를 더 잘 찾지만 메모리 오버헤드 비용이 더 높아진다.

제거된 큐는 작은 블록, 즉 특정 쓰레스홀드 하에 있는 블록에서만 사용된다. 큰 블록은 힙에서 직접 제거돼 너무 많은 메모리를 사용하지 않아도 된다. -free-queue-threshold=<value> 옵션을 사용해 큐에 메모리 블록을 놓는 쓰레스홀드 값을 지정할 수 있다. 기본 값은 10000바이트다. 이 값보다 큰 메모리 블록은 즉시 제거된다.

Purify는 프로그램 마지막에 모든 메모리 누수를 보고한다. 또한 사용되는 모든 메모리 블록을 보려면 -inuse-at-exit=yes 옵션을 사용해 이를 지정할 수 있다. 비슷하게 프로그램 마지막에 파일 서술자를 찾고자 한다면 -fds-inuse-at-exit=yes 옵션을 사용한다.

AIX 시스템에서 메모리 누수에만 관심이 있다면 -memory-leaks-only 옵션을 사용할 수 있고, Purify는 메모리 누수와 FMM(Freeing Memory Mismatch) 오류 같은 다른 힙 관리 오류만 찾는 아주 경량의 도구로 사용할 것이다. 이 경우 Purify는 각 메모리 접근을 인증하는 평소 작업을 하지 않기 때문에 프로그램은 훨씬 빨리 작동할 것이다.

요약

이 글을 통해 Purify 옵션과 지시어에 대해 배우고 이를 사용해 Purify 캐시, 실행 시간 오버헤드, 설치된 애플리케이션의 힙 메모리 풋프린트하는 방법을 배웠다. 이는 애플리케이션에서 필요한 작업을 위해 Purify를 커스터마이즈하는 데 도움이 될 것이다.



참고자료

교육

제품 및 기술 얻기

토론


필자소개

Satish Gupta 사진

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


Anand Gaurav 사진

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




기사에 대한 평가


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



 


 


12345
 


이 문서 북마킹 하기

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





위로


Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. 기타 회사, 제품, 및 서비스명은 다른 상표나 서비스 마크일 수 있습니다.

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