이 글은 솔라리스 썬 스튜디오 컴파일러에 있는 아주 유용한 오픈 소스 디버깅 패키지인 Dmalloc 에 대해 설명합니다.

순서

  • 소개
  • 왜 Dmaloc 이 필요할까요? 이미 전문적인 툴이 있지 않나요?
  • Dmalloc 제약사항
  • 솔라리스용 Dmalloc 빌드하기
  • Dmalloc 공유 라이브러리 사용하기
  • Dmalloc 을 이용해서 버그 수정하기
  • Dmalloc 이 어떻게 향상 될수 있을까요?
  • 결론
  • 참고자료
  • 감사의 인사
소개

메모리 접근과 관련된 어플리케이션 버그(예를 들어 메모리 덮어 쓰기)들을 찾는 것은 C/C++ 프로그래밍에서 가장 어렵고 노동집약적인 작업입니다. C 와 C++ 은 많은 기능들과 퍼포먼스들을 안겨 주지만 대신에 메모리 할당과 관련된 모든 상세 작업을 직접 다루어야 하는 과제도 줍니다.

메모리 관련 버그들에 의해 발생되는 크래쉬들 혹은 눈으로 보이는 문제점들도 있지만 그보다 더 심각하게 눈에 보이지 않는 버그들 때문에 예측하지 못한 순간에 커다란 문제를 야기할 수 있습니다. 또한 힙에 버퍼 오버플로우를 야기 시켜서 보안 결점을 노출시킬 수도 있습니다.

Dmalloc 은 오픈 소스 디버그 Malloc 패키지 입니다. 모든 소스코드와 문서들은 Dmalloc 웹 사이트(참고자료 [1] 에서 찾을 수 있습니다.

상대적으로 기술 수준이 낮고 간단한 디버깅툴들이 많이 존재 하지만 필자는 효율성, 상대적으로 간편한 사용성, 그리고 소스 코드를 포함한 상세 구현의 가능성 측면에서(필요에 따라 수정해서 사용할 수 있음) Dmalloc 을 좋아 합니다.

지난 몇년간 필자는 Dmalloc 을 셀 수 없이 많이 사용해 왔습니다. 이 툴은 필자가 X/Motif 라이브러리들 그리고 OpenGL 그래픽 파이프라인 같은 시스템 및 어플리케이션 코드에서 발견하기 매우 어려운 메모리 버그들을 찾는데 도움을 주었습니다.

Dmalloc 은 Gray Watson 에 의해 만들어지고 아직까지 유지보수 되고 있습니다. 1992년에 처음 나왔으므로 꽤 긴 시간이 지났습니다. 필자는 1997년에 처음 사용해 보았습니다.

Dmalloc 은 몇년간 계속해서 성장해 왔습니다. 그러나 여전히 상대적으로 간단하고 사용하기 쉽고 또한 가장 중요한 측면(적어도 필자의 의견으로는)으로 다른 툴들이 사용이 불가능하거나 확장성 혹은 다른 문제들에 의해 제대로 동작하지 않는 대용량 어플리케이션의 디버깅 시에 매우 효율적인 디버깅 툴입니다.

Dmalloc 의 솔라리스-특수한 이 글을 쓴데에는 Dmalloc 이 대부분 솔라리스 OS 가 아닌 플랫폼과 툴들에서 사용되어 왔기 때문입니다. 그러므로 솔라리스상에서 조화롭게 사용하려면 몇가지 tweak 과 설정 작업이 필요 합니다. 필자가 솔라리스에서 Dmalloc 을 쓰면서 겪었던 경험들도 같이 공유하도록 하겠습니다.

필자는 Dmalloc 을 SPARC, x64 기반 솔라리스에서(양쪽 모두에서 아주 유사하게 동작함) 썬 스튜디오 썬 스튜디오 11 컴파일러 와 같이 사용하였습니다.

왜 Dmaloc 이 필요할까요? 이미 전문적인 툴이 있지 않나요?

실제로 런타임시에 메모리 관련된 에러들을 찾는데 도움을 주는 많은 툴들이 존재 합니다. 그러나 그것들 모두는 완벽하지않습니다. 그러므로 Dmalloc 을 여러분의 디버깅 도구중에 하나로 추가시키는 것은 충분히 가치가 있습니다.

하나씩 차례대로 필자는 UNIX 시스템에서 사용가능한 대용 툴들에 대해 고려해 볼 것입니다. 그리고 이것들이 솔라리스/썬 스튜디오 환경에서 얼마나 유용한지에 대해서(필자의 개인적인 입장에서) 생각해보려고 합니다. 일단 썬의 툴들부터 시작해서 다른 것들을 고려해보도록 하겠습니다.

이 툴들은 매우 잘 알려져 있고 웹상에서 쉽게 찾을 수 있습니다. 그러므로 각각의 참고자료들은 제공하지 않을 것입니다.

dbx Run-Time Checking (RTC)

dbx RTC 는 썬 스튜디오 컴파일러/툴 스윗의 일부인 dbx 디버거의 일부분입니다. dbx DTC 는 다른 툴들에 비해 많은 장점들과 유용한 기능들을 가지고 있습니다. 그것이 동작하는 곳에서는 언제나 좋은 기능을 보여 줍니다. 필자는 중소 규모의 프로그램을 디버깅하는데 여러번 이것을 사용해 봤고 만족할만한 성공을 거두어 왔습니다.

현재 dbx DTC 메모리 접근 체크는 오직 SPARC 플랫폼의 솔라리스에서만 사용이 가능하고(x86/x64 플랫폼에서 불가능) 이것은 썬 스튜디오의 다음 버전에서 바뀔 것입니다.

dbx RTC 는 한가지 단점을 가지고 있습니다: 대용량의 어플리케이션으로의 확장이 어렵다는 것입니다. 이것은 dbx 에서 어플리케이션을 시작 시킬때 관계된 모든 어플리케이션과 시스템 바이너리들(실행 파일들과 공유 라이브러리들)을 메모리에 올리기 때문입니다.

libumem 패키지

libumem(3LIB) 은 malloc 의 교체 패키지 옵션으로 솔라리스(솔라리스 9 부터) OS의 일부로 제공되고 있습니다. 이것 또한 메머리 디버깅 모드를 가지고 있습니다. umem_debug(3MALLOC) 를 참고하시기 바랍니다 libumem 의 디버깅 기능은 "redzone" 섹션을 포함하고 있어서 메모리의 덮어쓰기를 체크할 수 있고 초기화 되지 않은 데이타를 감지해 내는데 도움을 주는 특별한 패턴을 이용해서 할당되거나 할당해지 된 메모리 세그먼트들을 채우는 기능을 제공 합니다.

그러나 umem_debug(3MALLOC) 을 이용한 몇번의 비과학적인 실험에서 Dmalloc 은 찾아낸 버그들을 찾아내지 못했습니다.

또한 umem_debug(3MALLOC) 을 사용하려면 mdb(1), "모듈러 디버거" 를 사용해야 하고 이것은 커널 프로그래머들이 사용하도록 의도되어진 프로그램 입니다. 이러한 점이 umem_debug(3MALLOC) 을 dbx 같은 심블록 디버거들을 사용해 왔던 어플리케이션 개발자들이 사용하는데 어렵도록 만듭니다.

Purify

Purify 는 IBM 의 상용 툴(Pure Software 다음에 Atria Software 다음에 Rational Software 에서 개발됨) 입니다. 이것은 가장 오래되고 가장 강력한 런타임 메모리 디버깅 툴중에 하나 입니다. 그러나 대용량의 솔라리스 어플리케이션을 디버깅할때에는 몇가지 단점이 있음을 발견했습니다:

  • 이 툴은 각 솔라리스 플랫폼 마다 즉 각 솔라리스 커널 업데이트와 새로운 컴파일러 버전 마다 특별한 버전이 필요함.
  • Purify 는 Purify 를 사용하기 위해 코드를 수정해서 어플리케이션을 재링크해야 하는 작업이 필요함.
  • 앞의 결과로 특별히 생성된 바이너리들은 오직 그들이 빌드된 곧에서만 실행이 가능함.
  • 생성된 바이너리들의 실행할때의 매우 느려질 수 있음. 몇몇 어플리케이션의 경우 수분에서 하루종일 걸리는 것을 본적이 있음.
  • 이 툴은 거짓-양성 판정을 많이 생성해서 실제 메모리 버그와 나머지들의 구분을 어렵게 함.
  • 이 툴은 VIS 명령어(SPARC 플랫폼에서) 와 호환되지 않음. 예를 들어 mediaLib 함수를 호출하는 어플리케이션들 과의 사용이 불가능함.
  • 이 툴은 상용(그리고 상당히 비쌈)임. 돈이 들 뿐만 아니라(요즘 같은 오픈 소스와 "무료" 소프트웨어의 시대에서 큰 장벽이 됨) Prufiy 라이센싱을 추가 하는 것이 디버깅 프로세스를 상당히 복잡하게 함.
Valgrind

Valgrind 는 매우 강력한 오픈 소스 툴로써 런타임 메모리 체크에 사용됩니다. 이 툴은 오나벽하진 않지만 사용이 가능한 상황에서(주로 리눅스/x86 시스템에서) 매우 유용합니다.(또한 매루 빠릅니다.)

불행하게도 Valgrind 는 솔라리스에서, x86/x64 에서 조차 사용이 불가능 합니다.(SPARC 은 언급할 필요도 없음) Valgrind 는 x86 아키텍처의 상세 아키텍쳐(각 x86 명령어들을 에뮬레이트함), 리눅스 커널 GLIBC, 그리고 GNU C 컴파일러와 서로 강력히 연결되어 있으므로 솔라리스로 포팅 하는것이 매우 어렵습니다.

기타

런타임 메모리 디버깅이 가능한 많은 툴들이 상용 혹은 오픈 소스 형태로 존재 합니다: Electric Fence, GNU Checker, Insure++, Mpatrol, Etnus MemoryScape 등이 있습니다. 이툴 들은 사용해 보지 않아서 커멘트를 할 수 없습니다.

이러한 툴들의 대한 목록은 Mpatrol 웹 사이트의 Related Software 섹션 에서 참고 바랍니다.

Dmalloc 제약사항

Dmalloc 은 malloc-대체 패키지 입니다. 이것은 다른 패키지들 처럼 제약 사항들이 있습니다. 특별히:

  • Dmalloc 은 오직 힙에서의 메모리-관련 문제만들 감지 할 수 있으며 스택 혹은 정적 메모리에서의 문제점은 감지해낼 수 없음.
  • 메모리가 malloc() 에 의해 할당되었을때만 버그를 감지해낼 수 있으며 sbrk() 혹은 mmap() 에 의해 할당됐을 경우 사용이 불가능함.
  • 아래와 같은 문제점들은 감지해 낼 수 없고 이것은 dbx DTC, Purify 그리고 Valgrind 같은 다른 좀더 복잡한 툴들(malloc, realloc, free 의 교체 보다 훨씬 더 많은 기능을 제공하는) 에 의해서 가능 함:
    • 스택 메모리 체크
    • 할당되지 않은 메모리 읽기 혹은 쓰기
    • 할당됐지만 초기화 되지 않은 메모리 읽기
    • 읽기전용 메모리에 쓰기
솔라리스용 Dmalloc 빌드하기

요구사항에 따라서 Dmalloc 은 여러가지 방법으로 사용될 수 있습니다. Dmalloc 은 정적 라이브러리 혹은 공유 라이브러리로 사용될 수 있습니다. 또한 선택적으로 #include dmalloc.h 를 이용해서 Dmalloc 의 추가적인 기능을 사용할 수 있습니다. "멀티쓰레드 버전", "C++ 버전," 이 필요할 수도 필요하지 않을 수도 있습니다. Dmalloc 은 오직 C 헤더파일에서만 지정할 수 있는 설정 옵션을 제공하므로 리빌드가 필요 합니다.

그러므로 모든 경우에 대한 바이너리를 제공하는 것은 실용적이지 않습니다 그리고 여러분의 특정 needs 에 맞는 Dmalloc 을 빌드 하는것이 필요합니다.

비록 Dmalloc 이 정상적으로 빌드 되고 실행되는 플랫폼 목록에 솔라리스가 등재되어 있지만 실제로 솔라리스에서 썬스튜디오를 이용해서 Dmalloc 을 빌드 하는 것은 쉬운 일은 아닙니다.

한가지 특별한 예를 들어 Dmalloc 은 ./configure 장치를 사용하는데 이것은 GNU Autoconf 스윗의 일부로 오픈 소스 프로그램을 다양한 플랫폼에서 빌드하는데 사용 됩니다. 문제는 GNU Autoconf 는 GNU 컴파일러, 툴 그리고 운영체제 예를들어 리눅스나 FreeBSD 같은 것들을 위해 디자인 되었고 당연히 솔라리스는 고려되지 않았습니다. 이러한 운영체제 기능들과 컴파일러에 상당한 의존성이 존재합니다.

GNU Autoconf 를 수정하고 ./configure 스크립트를 솔라리스와 썬 스튜디오 컴파일러에 맞게 수정하고 Dmalloc 에 의해 이러한 툴이 사용되는 방법을 수정하는 것은 이 글의 범위를 벗어나는 작업입니다. 필자는 이러한 어려운 방법을 진행할 시간과 인내심이 없었습니다. 그래서 이 글에서는 솔라리스10 이 돌아가고 있는 SPARC 시스템에서 대규모 어플리케이션의 메모리 관련 버그를 찾는데에 맞는 필자가 최근에 사용한 "해결책" 에 대해 설명하려고 합니다.

이 특별한 케이스를 위해 사용한 단계들입니다.

1. Dmalloc 패키지 다운로드 및 인스톨을 한다.

필자는 2007년 2월에 배포된 최신버전 5.5.0 을 사용했습니다. 패키지를 unzip 하기 위해 다음과 같은 명령어를 사용했습니다:

% cd where_you_want_to_install_it
% gunzip -c /tmp/dmalloc-5.5.0.tgz | tar xvf -

이 작업은 설치하고자 하는 디렉토리/dmalloc-5.5.0 라는 이름의 서브디렉토리를 생성할 것이고 모든 Dmalloc 파일들을 위치시킬 것입니다.

설치 명령어들은 보통 Dmalloc 웹사이트의 How to Install the Library 문서에 있는 명령어들을 사용했습니다.

2. settings.dist 파일을 수정한다.

저의 경우는 다음과 같이 수정했습니다:

ALLOW_FREE_NULL_MESSAGE=0
FENCE_TOP_SIZE=16
FENCE_BOTTOM_SIZE=ALLOCATION_ALIGNMENT*2
LOG_REOPEN=0

이러한 셋팅값들의 상세 정보는 Dmalloc 문서들에 설명되어 있습니다. 그러나 가장 중요한 것은 FENCE_TOP_SIZE=16FENCE_BOTTOM_SIZE=ALLOCATION_ALIGNMENT*2 이고 이것은 "picket-fence" ("redzone" 이라고도 불림) 영역을 각 malloc 블럭 할당 부분의 양 가장자리에 16 바이트로 설정합니다. 실행되는 동안 Dmalloc 은 이 공간들의 무결성을 체크하고 덮어 씌여졌는지를 감지 합니다.

3. configure 실행하기.

이 경우에 필자는 어떠한 특별 C++ 설비들도 필요로 하지 않았습니다. "쓰레드" 버전(Dmalloc 이 그의 특별한 malloc, realloc, 그리고 free 루틴에서 뮤텍스(mutex) 락킹을 써야 한다는 의미임), 이 필요로 했고 공유 라이브러리 버전을 통해서 LD_PRELOAD_64 를 사용할 수 있어야 했습니다. 또한 64-비트 버전의 라이브러리가 필요로 했는데 그래야만 64-비트 어플리케이션에 사용할 수 있었기 때문입니다.

기본적으로 configure 는 GCC 를 컴파일러로 가정합니다. 썬 스튜디오 컴파일러를 사용하기 위해서 CC 환경변수를 설정했고 다음과 같은 명령어들을 사용 했습니다.

% setenv CC "/opt/SUNWspro/bin/cc -KPIC -errfmt=error -Xc -xarch=v9"
% ./configure --disable-cxx --enable-threads --enable-shlib

컴파일러 플래그가 올바른지 확인하기 위해 몇번의 시행착오를 거쳤습니다. 특히 -Xc 가 꼭 필요한 걸로 판명 났는데 왜냐하면 Dmalloc 은 오직 컴파일러가 ANSI-C 호환 일때에만 제대로 동작하기 때문이고 썬 스튜디오 컴파일러의 -Xc 플래그가 바로 그와 같은 역활을 합니다. -KPIC 설정은 공유 라이브러리 때문에 지정했고 위치-독립적인 코드를 생성하기 위함입니다.

-xarch=v9 설정은 SPARC 시스템의 64-비트 솔라리스 어플리케이션을 위한 것입니다. 64-비트 솔라리스 x64 어플리케이션을 위해서는 -xtarget=opteron -xarch=amd64 를 대신 사용했습니다.

불행하게도 configure 실행시 많은 문제들이 생겼습니다. 일단 썬 스튜디오 컴파일러를 어떻게 다뤄야 할지 알려 주지 않았기 때문에 많은 수의 테스트 들이 실패 했고 프로그램 conftest (32-비트 64-비트 버전 둘다에서) 가 크래쉬 됐습니다. 본인은 좀 더 빠르게 알 수 있었는데 왜냐하면 저의 머신은 시스템-와이드 한 AppCrash (참고자료 [2] , [3]) 가 설치되어 있었기 때문입니다. 결과적으로 다음과 같은 내용을 AppCrash 로 부터 메일로 받았습니다:

Output from runme_on_app_crash
Program: conftest
Process ID: 8389
Received signal: 6

Application Debugging Data
--------------------------

>> /bin/pstack 8389

8389:	./conftest

 ffffffff7f2ce8c4 _lwp_kill (6, 0, ffffffffffffffff, \
   ffffffff7f3e6000, 0, 0) + 8
 ffffffff7f248bcc abort (1, 1b8, ffffffff7f2ba83c, \
   19d540, 0, 0) + 118
 0000000100000de0 main (1, ffffffff7ffff1f8, ffffffff7ffff208, \
   ffffffff7f249408, ffffffff7f1000c0, ffffffff7f100100) + 28
 0000000100000a9c _start (0, 0, 0, 0, 0, 0) + 17c

...

>> /bin/ptree 8389
...
801   /bin/csh
 7086  /bin/bash ./configure --disable-cxx --enable-threads --enab
   8388  /bin/bash ./configure --disable-cxx --enable-threads --en
     8389  ./conftest
...

다른 한편으로는 abort() 의 크래쉬는 의도적일 수도 있고 configure 스크립트에 의해 수행되는 테스트의 일부일 수도 있음이 들어 났습니다. AppCrash 가 감지해 냈기 때문에 필자만이 이 크래쉬를 알아 챌 수 있었을 수도 있습니다.

어떠한 경우에든 configure 는 필요한 파일들을 생성해 냅니다: Makefile, conf.h, 그리고 settings.h. Dmalloc 문서에 쓰여진 대로 파일들의 무결성을 검사해 보았습니다.

4. make 실행하기.

이 작업은 몇몇 컴파일러 warning (해로워 보이지는 않는) 을 보여주고 동시에 libdmallocth.so 라고 하는 공유 라이브러리가 필요하다고 알려 줍니다. 그러나 이 라이브러리는 잘못된 것임이 드러났습니다. 그것은 32-비트 였습니다. (필자가 필요로 한것은 64-비트 였음):

% file libdmallocth.so
libdmallocth.so:  ELF 32-bit MSB dynamic lib SPARC Version 1,
dynamically linked, not stripped, no debugging
information available

또한 어떠한 함수 정의도 들어있지 않았습니다:

% nm libdmallocth.so | grep FUNC
%

make 출력의 내용을 살펴 보면 라이브러리가 어떤식으로 빌드 됐음을 알 수 있습니다:

ar cr libdmallocth.a arg_check.o compat.o dmalloc_rand.o \
  dmalloc_tab.o env.o heap.o chunk_th.o error_th.o malloc_th.o
ranlib libdmallocth.a
rm -f libdmallocth.so libdmallocth.so.t
ld -G -o libdmallocth.so.t libdmallocth.a # arg_check.o \
  compat.o dmalloc_rand.o dmalloc_tab.o env.o heap.o chunk_th.o \
  error_th.o malloc_th.o
mv libdmallocth.so.t libdmallocth.so

이것은 전부 잘못됐습니다. 예를 들어 ranlib(1) 커맨드는 예전 SunOS 4.x OS 버전의 명령입니다. 이 경우에서는 어카이브 라이브러리를 생성할 아무런 이유도 없습니다.

수정을 위해 간단히 cc 커맨드를 사용했습니다:

% /opt/SUNWspro/bin/cc -xarch=v9 -G -o libdmallocth.so \
  arg_check.o compat.o dmalloc_rand.o dmalloc_tab.o env.o heap.o \
  chunk_th.o error_th.o malloc_th.o
% file libdmallocth.so
libdmallocth.so:   ELF 64-bit MSB dynamic lib SPARCV9 Version 1, \
  dynamically linked, not stripped
% nm libdmallocth.so | grep FUNC
[747]   |  61928|  140|FUNC |GLOB |0   |7  |_dmalloc_address_
break
[751]   |  45936|  184|FUNC |GLOB |0   |7  |_dmalloc_atoi
...

후에 이것을 좀더 쉽게 하기 위해 make 커맨드 뒤에 cc 커맨드를 실행하는 rebuild 라는 스크립트를 생성했고 리빌드가 필요로 할때 마다 rebuild 스크립트를 사용했습니다.

5. 테스트 프로그램 실행하기.

Dmalloc 문서("make light") 의 테스트를 필자는 사용할 수 없었습니다.

SPARC 플랫폼의 솔라리스10에서 dmalloc_t 프로그램은 시스템상의 모든 메모리를 다 소비한 다음 시스템을 멈춰버립니다. dmalloc_t 프로세스를 강제종료 시켜야 했습니다.

x64 플랫폼의 솔라리스10에서 테스트 프로그램 dmalloc_t 은 다음과 같은 에러 메세지를 보여 줍니다:

  % ./dmalloc_t -s -t 10000
  ERROR: Running special tests failed.  Last dmalloc error: no
error (err 1)
  Random seed is 1173381022. Final dmalloc error: no error (err 1)

dmalloc_t-s 플래그 없이 실행시키면(non-slient 모드에서) 다음과 같은 에러메세지를 보여 줍니다:

  ERROR: index overload failed
  ERROR: index overload failed

이러한 에러 메세지들과 조건들이 무엇을 의미 하는지 알 수가 없었습니다. 단지 테스트 프로그램 dmalloc_t.c 에 문제가 있다는 것만 나타냈으므로 더이상의 디버깅을 중지 했습니다.

Dmalloc 공유 라이브러리 사용하기

Dmalloc 공유 메모리를 사용하기 위해 다음과 같은 단계를 수행했습니다.

1. DMALLOC_OPTIONS 환경 변수를 설정.

Dmalloc은 많은 옵션들을 가지고 있습니다. 이러한 옵션들을 하나의 커맨드로 읽기 좋게 만드는 것은 불가능에 가깝습니다. 대신 Dmalloc 은 dmalloc 이라고 하는 유틸리티 프로그램을 가지고 있습니다. dmalloc 을 사용하는 방법은 여러가지가 있습니다. 자세한 정보는 Dmalloc 웹 사이트의 Description of the Debugging Tokens 섹션을 참고하시기 바랍니다.

필자가 사용한 예를 보여 드리겠습니다.

dmalloc-5.5.0 디렉토리에서 .dmallocrc 을 생성하고 샘플 파일 dmallocrc 도 복사 했습니다. 그 다음 .dmallocrc 수정해서 greg 이라는 이름의 섹션을 사용하고자 하는 Dmalloc 옵션들을 포함 시켜서 생성했습니다:

greg   log-bad-space, check-fence, check-heap, \
       check-funcs, print-messages, error-dump, \
       realloc-copy, check-blank

그 다음 dmalloc greg 을 실행시킨 결과 다음과 같은 출력을 얻었습니다("feature has been disabled" warning 은 무시하시기 바랍니다. ):

setenv DMALLOC_OPTIONS debug=0x42106d00

16진수 debug 값에 부가적으로 몇가지 옵션을 추가 시켜서 최종적으로 DMALLOC_OPTIONS 은 다음과 같이 결정됐습니다:

setenv DMALLOC_OPTIONS debug=0x42106d00,inter=1000,log=logfile.%p

이 커맨드에서 inter=1000 은 기본값인 inter=1 이 전체 힙을 매번 확인 하는 것과는 반대로 malloc, realloc, 혹은 free 의 천번째 호출 마다 힙의 무결성을 체크 한다는 의미 입니다. log=logfile.%p 설정은 로그 파일을 logfile.pid 형태로 생성하도록 하며 여기서 pid 는 프로세스 ID 로 대체됩니다.

2. LD_PRELOAD_64 환경변수를 Dmalloc 공유 라이브러리로 설. 예를 들어:

setenv LD_PRELOAD_64 /export/home/dmalloc-5.5.0/libdmallocth.so

3. 어플리케이션을 실행.

어플리케이션이 동적으로 링크 된 malloc 패키지 와 표준 libc malloc(3C) 을 사용하고 있는지 확인합니다. preloading 은 만약 malloc, realloc, 과 free 가 정적으로 링크되었다면 동작하지 않을 것입니다.

Dmalloc 을 이용해서 버그 수정하기

간단한 예제를 살펴 봅시다. 이번 경우에서 Dmalloc 은 울트라45 워크스테이션에 설치된 XVR-2500 그래픽 카드의 OpenGL 파이프라인에서 2가지의 문제점을 감지했습니다. 이 문제들 자체는 거의 하찮은 문제이지만 매우 전형적으로 발생하는 문제들 입니다.

1. 메모리 덮어쓰기:

1168279430: 27537:   pointer '0x10d0bf910' from 'unknown' \
  prev access 'unknown'
1168279430: 27537:   dump of proper fence-top bytes: \
  '\372\312\336i\372\312\336i\372\312\336i\372\312\336i'
1168279430: 27537:   dump of '0x10d0bf910'+3: \
  'v/fb\000\312\336i\372\312\336i\372\312\336i\372\312\336i'
1168279430: 27537:   next pointer '0x10d0bf940' (size 8) may \
  have run under from 'unknown'
1168279430: 27538: ERROR: _dmalloc_chunk_heap_check: failed \
  OVER picket-fence magic-number check (err 27)

스택 트레이스 (AppCrash 에 의해 보고된) 는:

...
ffffffff7ee17470 dmalloc_error (ffffffff7ee1b3f0, \
  ffffffff7fffac48, 0, ffffffff76bf2044, 0, 0) + 140
ffffffff7ee11a7c log_error_info (0, 0, 0, 10d0b0a98, \
  ffffffff7ee1b3d8, ffffffff7ee1b3f0) + 3c
ffffffff7ee13ff0 _dmalloc_chunk_heap_check (0, ffffffff7f72fd5c, \
  ff000000, ff000000, 11e6f0, ffffffff7f72c448) + 5f8
ffffffff7ee1806c dmalloc_in (0, 0, 1, ffffffff7f61f068, 11e6a0, \
  ffffffff71602000) + 3ec
ffffffff7ee18370 dmalloc_malloc (0, 0, 20, a, 0, 0) + 40
ffffffff7ee18cd0 malloc (20, ffffffff6bacc238, 0, \
  ffffffff6b609538, 11e6f0, ffffffff7f72c448) + 28
ffffffff78b055d0 XextAddDisplay (ffffffff6c685190, 10d864010, \
  ffffffff6c67d078, ffffffff6c648728, 0, 0) + 20
ffffffff6b609538 ogl_kfb_XF86DRI_glx_QueryDirectRenderingCapable \
  (10d864010, 10d090e10, ffffffff7fffb854, 2238, ffffffff6c648718, \
  ffffffff7f61f068) + 78
ffffffff6b607964 ogl_kfb_XF86DRIQueryDirectRenderingCapable \
  (10d864010, ffffffff6b6094c0, ffffffff7fffb854, ffffffff6b5f6910, \
  4b93dc, 0) + 24
ffffffff6b5eed20 __driCreateScreen (10d864010, 0, 10d090e68, \
  b, 10d88f010, 0) + 20
ffffffff6b5f6960 ogl_kfb_create_screen (10ce5c810, 8000, \
  10d0bf3d0, 10daa7900, 1, 10d88f010) + 758
ffffffff79ab7080 __glxcLoadInitModule (10d091210, 0, 0, \
  230, 0, ffffffff79d19a40) + 640
ffffffff79ab71d4 cglxdCreateContext (10d864010, 10daa7690, 0, \
  10daaa010, 8014, 10ce5c810) + 94
ffffffff79af6e2c __glXCreateNewContext (10d864010, 10daa7690, \
  8014, 1, 0, 0) + 5ec
ffffffff7f1532b8 _glXCreateNewContext (10d864010, 10daa7690, 8014, \
  0, 1, ffffffff7f30b7b8) + 254
ffffffff7f131c9c glXCreateContext (10d873010, 10daa9810, 0, 1, \
  1, ffffffff7fffc190) + d94
...

Dmalloc 문서에 따르면:

27 (ERROR_OVER_FENCE)
  This indicates that a pointer had its upper bound
picket-fence magic space overwritten. If the 'check-fence'
token is enabled, the library writes magic values above and
below allocations to protect against overflow. ...

이 에러에 대해서는 아래에 좀더 자세히 다루도록 하겠습니다.

2. 2중 free():

1168468189: 27858: ERROR: free: cannot locate pointer in \
  heap (err 22)
1168468194: 27858:   error details: finding address in heap
1168468194: 27858:   pointer '0x10d0c6c10' from 'unknown' prev \
  access 'unknown'

좀 더 자세한 정보를 얻기 위해 필자는 코드를 변경해서 이러한 메세지들을 출력하는 코드들을 (error.c 파일의 dmalloc_error() 루틴) fork() 대신 pstack(1) 을 출력하도록 했습니다. 수정사항은 아래에 좀더 자세히 설명되어 있습니다.

다음과 같은 스택 트레이스가 생성되었습니다:

8977: /<path_to_executable>
-----------------  lwp# 1 / thread# 1  --------------------
  ffffffff76ccebb8 waitid   (0, 2316, ffffffff7fffae90, 3)
  ffffffff76cc0cec waitpid (2316, ffffffff7fffb110, 0, 0, \
    ffffffff6f713a40, 0) + 64
  ffffffff76cb44ac system (ffffffff7fffb2b8, 1988, 1800, 0, \
    ffffffff76de6000, ffffffff7fffb178) + 394
  ffffffff7ef172d8 dmalloc_error (ffffffff7ef1b3c8, \
    ffffffff76df0fc0, 0, 1000, 0, 0) + 140
  ffffffff7ef11a04 log_error_info (0, 0, 10d0c6c10, 0, \
    ffffffff7ef1b3b0, ffffffff7ef1b3c8) + 3c
  ffffffff7ef14ca4 _dmalloc_chunk_free (0, 0, 10d0c6c10, 11, \
    0, 0) + 1dc
  ffffffff7ef18910 dmalloc_free (0, 0, 10d0c6c10, 11, 4cd000, \
    30000) + 108
  ffffffff7ef18ed0 free (10d0c6c10, 10d0c6b10, 1, 10d0c6b10, \
    0, 10d0c6c10) + 20
  ffffffff6b5f4854 ogl_kfb_destroy_drawable (10db79890, \
    10d080e10, 10d0c6c10, 7800, 2, ffffffff6b5ee008) + 58
  ffffffff79ca84c4 cglxdDestroyGlxDrawable (10ce5c810, 0, 0, \
    10d0c6d10, ffffffff6b5f48f0, 0) + 324
  ffffffff79cafec0 __glXDestroyPbuffer (10d8c3010, b00010, \
    400, ffffffff7f214b54, 22f100, 420) + 40
  ffffffff7f214b54 glXDestroyPbuffer (10d8c3010, b00010, 0, \
    ffffffff7f3e5de0, ffffffff7f3e9ad3, ffffffff7f3e9a70) + 7c4
  ffffffff7f272a78 __1cHpbuffer2T5B6M_v_ (10d961780, 0, \
    ffffffff7f3e9a70, ffffffff7f3e9ace, ffffffff7f3e9ba8, \
    ffffffff7f3e5de0) + 480
  ffffffff7f2735bc __1cFpbwin2T5B6M_v_ (10d774af0, \
    10d774af8, 10d961780, ffffffff7f3e5de0, 0, 0) + 2c
  ffffffff7f26b4cc __1cHwinhashGdetach6MpnP__winhashstruct__v_ \
    (10d774af0, 10cf1e7e8, 0, 1000, 0, 6) + 34
  ffffffff7f264654 __1cI_winhashGremove6MpcLb_v_ (10ce744c0, \
    10d0af2d0, 5400002, 0, ffffffff7f3e5de0, 0) + 264
  ffffffff7f22d588 XDestroyWindow (10d8c2010, 5400002, \
    10ce744c0, ffffffff7f3eac68, ffffffff7f3e5de0, 0) + 7d8
...

결국 2중의 free() 메모리 에러가 코드에 있었음이 판명되었습니다.

이러한 문제들은 썬 스튜디오 dbx 혹은 IDE 같은 디버거들을 사용해서 좀더 디버깅 될 수 있습니다. dmalloc_error() 에 브레이크포인트를 설정하고 함수의 매개변수들을 검사하거나 힙의 내용을 검사하는 등의 작업을 할 수 있습니다. 물론 어플리케이션이 디버깅 가능하도록 컴파일 되었고 소스 코드가 열람이 가능하다면 이러한 작업은 훨씬 쉬워질 것입니다.

이러한 버그들은 썬 OpenGL 패치의 다음 버전에서 모두 수정되었습니다.

Dmalloc 이 어떻게 향상 될수 있을까요?

생각할 수 있는 몇가지 Dmalloc 향상 방법을 소개 합니다.

솔라리스에서 썬 스튜디오 컴파일러를 인식하는 Dmalloc 구현체 만들기

이것은 64-비트(SPARC, x86 플랫폼 둘다) 썬 스튜디오 컴파일러 플래그와 ANSI-C 를 포함해야 합니다.cc 혹은 CC 로 링킹하기 위해 ld 커맨드를 교체해야 하는 등의 작업이 필요합니다.

위에서 언급한 ./configure 문제는 autoconf, automake, 혹은 libtool 같은 툴들 자체와 크게 관련된것 같지는 않습니다. 대신 Dmalloc 에서 이러한 툴들이 사용되는 방법과 관련이 있어 보입니다.

Dmalloc 은 공유 라이브러리를 생성해야 하기 때문에(적어도 옵션으로 라도) libtool 을 사용할 수 있어야 됩니다. 그러나 현재의 configure.ac 파일은 AC_PROG_LIBTOOL 매크로를 호출하지 않습니다. 대신 고유의 매크로를 가지고 있어서 공유 라이브러리를 빌드 하는 방법을 정의하고 있고 이러한 것들은 솔라리스와 썬 스튜디오 컴파일러에 적당하지 않습니다. 썬 스튜디오 컴파일러를 이용해서 cc (혹은 C++ 은 CC) 컴파일러 드라이버를 이용해서 공유 라이브러리를 링크해야 합니다. 그래서 64-비트 혹은 32-비트 라이브러리의 생성이 일관적이어야 합니다. 대신에 ld 를 직접적으로 호출해야 하고 64-비트 라이브러리를 원할때 -64 를 설정하지 않아야 합니다.

Makefile.am 또한 존재하지 않으므로 automake 를 안쓰는 것은 확실합니다. 대신에 손으로 직접 작성한 Makefile.in 파일을 제공합니다.

프로젝트를 libtool 을 사용하는 것으로 전환이 요구 됩니다. 적어도 솔라리스에서 libtool-1.5.22 혹은 그 이후의 버전은 썬 컴파일러로 공유 라이브러리를 빌드 할때 제대로 동작해야 합니다.

Dmalloc 공유 라이브러리 64-비트 버전을 빌드 할때 옵션 추가하기

현대의 어플리케이션들은 64-비트인 경향이 많습니다. 사용자들은 이러한 두가지를 선택할 수 있는 손쉬운 방법이 필요 합니다.

mmap() 문제 수정

Dmalloc 이 어플리케이션에서 malloc 대신 mmap(2) 을 쓰기 시작하는 상황을 본적이 있습니다. 이문제를 해결하기 위해 필자는 Dmalloc 소스 파일 heap.c에서 #if HAVE_MMAP && USE_MMAP 섹션을 주석 처리 했었습니다:

% diff heap.c.orig heap.c
97c97
< #if HAVE_MMAP && USE_MMAP
---
> #if HAVE_MMAP && USE_MMAP && 0

덮어씌여진 메모리 덤프의 가독성 증가시키기 및 덤프가 포함하고 있는 것이 어떠한 것인지 설명하는 문서의 향상

현재 덤프된 데이타는 배부분 8진수 포맷으로 되어 있습니다. 제 생각에는 이것을 16진수로 포맷팅 한다면 유저들이 이해하기 좀 더 쉬울 것입니다.

위에서 언급한 첫번째 예제를 사용하면 메모리 덮어쓰기 에러 메세지는 다음과 같았었습니다:

1168279430: 27537:   pointer '0x10d0bf910' from 'unknown' \
  prev access 'unknown'
1168279430: 27537:   dump of proper fence-top bytes: \
  '\372\312\336i\372\312\336i\372\312\336i\372\312\336i'
1168279430: 27537:   dump of '0x10d0bf910'+3: \
  'v/fb\000\312\336i\372\312\336i\372\312\336i\372\312\336i'
1168279430: 27537:   next pointer '0x10d0bf940' (size 8) \
  may have run under from 'unknown'
1168279430: 27538: ERROR: _dmalloc_chunk_heap_check: failed \
  OVER picket-fence magic-number check (err 27)

"proper fence-top bytes" 는 0xFACADE69 로 초기화 됐고(file chunk_loc.h 에 정의된 대로) 4번의 초기화가 일어 났습니다. 왜냐하면 Dmalloc 이 16-바이트 fence(redzone으로 불리는) 를 가지도록 설정했고 각각 "하위", "상위" 에 가지도록 설정했기 때문입니다. ASCII 로 디스플레이될 수 없는 문자들의 8진 표현은 다음과 같이 번역됩니다:

\372\312\336i\372\312\336i\372\312\336i\372\312\336i

덮어 쓰여진 버퍼는:

\000\312\336i\372\312\336i\372\312\336i\372\312\336i

그러나 Dmalloc 메시지에서 이부분이 확실하지는 않습니다. 필자는 오랜 시간이 경과후에 이것을 꺠달았고 Dmalloc 이 정확히 무슨 작업을 했는지 확인 해야 했습니다. 파일 chunk.c 에서:

/*
 * The size includes the bottom fence post area.  We want it to
 * align with the start of the top fence post area.
 */
if (DUMP_SPACE > user_size + FENCE_OVERHEAD_SIZE) {
  dump_size = user_size + FENCE_OVERHEAD_SIZE;
  offset = -FENCE_BOTTOM_SIZE;
}
else {
  dump_size = DUMP_SPACE;
/* we will go backwards possibly up to FENCE_BOTTOM_SIZE offset */
  offset = user_size + FENCE_TOP_SIZE - DUMP_SPACE;
}
...
dump_pnt = (char *)start_user + offset;
if (IS_IN_HEAP(dump_pnt)) {
  out_len = expand_chars(dump_pnt, dump_size, out, sizeof(out));
  dmalloc_message("  dump of '%#lx'%+d: '%.*s'",
	  (unsigned long)start_user, offset, out_len, out);
}

이 경우의 값들은 다음과 같습니다.

DUMP_SPACE = 20
user_size = 7 (length of "/dev/fb")
FENCE_OVERHEAD_SIZE = 16
offset = user_size + FENCE_TOP_SIZE - DUMP_SPACE = 3

그러므로malloc() 은 7바이트를 요청하기 위해 호출 되었습니다. 그러나 8 바이트가 버퍼에 쓰여 졌고 제일 끝에 0이 붙어 나왔습니다: /dev/fb\000. 이 예제에서는 다음과 같은 OpenGL 코드의 결과 입니다 ( devPath 는 캐릭터 스트링으로 /dev/fb 를 포함함):

char *ptr;
...
ptr = malloc(strlen(devPath));
strcpy(ptr, devPath);

이 코드의 작성자는 strcpy() 가 복사된 스트링의 끝에 0을 집어 넣는 다는 사실을 잊고 있었습니다. 이것은 C/C++ 에서 자주 발생되는 에러 입니다. 이러한 상황에서 올바른 malloc() 호출 방법은:

ptr = malloc(strlen(devPath)+1);

사용자가 이러한 형태의 에러에 좀 더 쉽게 대처할 수 있도록 좀 더 자세한 디버깅 정보를 프린트 하는 것이 중요하다고 생각됩니다. 즉 현재 할당 된 사이즈 (user_size) 와 DUMP_SPACE 값등을 제공하는 것 말입니다. 아마도 손상된 "fence" 자체와 더불어 현재 무엇을 프린트 하는 것인지 같이 제공해 준다면 많은 도움이 될 것입니다.

서로 겹치는 메모리 지역에 대한 memcpy() 호출 체크 추가하기

최신 버전의 Dmalloc (5.5.0)은 이러한 기능을 이미 가지고 있었습니다. 그러나 필자는 솔라리스 머신에서 썬 스튜디오 컴파일러에서 조차 한번도 제대로 동작시켜본 적이 없습니다. dmalloc.h 를 테스트 프로그램에 포함시켰을때도 역시 마찬가지 입니다.

이 이슈는 malloc 과 직접적으로 관계되어 있지는 않습니다. 그러나 추가적인 체크는 매우 유용하고 하기도 간단합니다. 예전에 Dmalloc 포럼의 어떤 사람이 이것을 구현하도록 제안한 적이 있습니다. Valgrind 는 이러한 체크가 가능합니다.

문제는 memcpy() 는 종종 소스 와 목적 메모리 버퍼가 서로 겹쳐질때 사용된다는 것입니다. 이것은 적어도 솔라리스에서는 버그 입니다. 이것은 메모리 버퍼에 문제를 야기할 수 있습니다. 이러한 경우에는 상대적으로 비효율적이지만 memmmove() 를 호출 해야 합니다.

현재 필자는 Dmalloc 과 별도로 이러한 체크를 할 수 있도록 특별한 라이브러리 삽입자를 만들었습니다. 참고자료 [4] 에서 라이브러리 삽입자에 대해 좀 더 알아보시기 바랍니다. 여기에 라이브러리 삽입자 소스 코드가 있습니다:

% cat memcpy_interp.c
/*
 * Interpose on memcpy() and check for overlapping memory
 * segments, like Valgrind.
 * By Greg Nakhimovsky, Sun Microsystems.
 * January 2007.
 *
 * Build and use this interposer as following
 * (assuming 64-bit application on Solaris/SPARC):
 * cc -g -errfmt=error -xarch=v9 -o memcpy_interp.so -G
 * -Kpic memcpy_interp.c
 * setenv LD_PRELOAD_64 /path/memcpy_interp.so
 * run the app
 * unsetenv LD_PRELOAD_64
 */

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>
#include <string.h>

void *memcpy(void *restrict s1,  const  void  *restrict  s2,
     size_t n)
{
  static void * (*func)(void *restrict s1,  const  void
    *restrict  s2, size_t n);
  static char buffer[64];
  char *cs1 = s1;
  char *cs2 = s2;
  int x;

  if(!func)
  {
    func = (void *(*)()) dlsym(RTLD_NEXT, "memcpy");
    sprintf(buffer,"LD_PRELOAD_64= /bin/pstack %ld\n", getpid());
  }

  x = cs2 - cs1;
  if(x < 0) x = - x;
  if(x < n)
  {
     printf("\nmemcpy() called with overlapping segments:\n
       s1=0x%p s2=0x%p n=%d\n", s1, s2, n);
     system(buffer);
  }

  return func(s1,s2,n);
}
%

흥미롭게도 문제가 있는 어플리케이션을 이 라이브러리 삽입자와 함께 실행 시키면 (Dmalloc 을 빼고) X/Motif 의 GetResources() 루틴이 비효율적임을 감지해 냅니다. 아래와 같은 결과를 무수히 많이 얻었습니다.

memcpy() called with overlapping segments:
  s1=0x1253f0788 s2=0x1253f0788 n=8

19436: /path/app.exe
-----------------  lwp# 1 / thread# 1  --------------------
  ffffffff76dcebb8 waitid   (0, 4f3e, ffffffff7fff7d30, 3)
  ffffffff76dc0cec waitpid (4f3e, ffffffff7fff7fb0, 0, 0, \
    ffffffff6f818480, 0) + 64
  ffffffff76db44ac system (ffffffff7f300960, 1988, 1800, 0, \
    ffffffff76ee6000, ffffffff7fff8018) + 394
  ffffffff7f200580 memcpy (1253f0788, 1253f0788, 8, ffffff67, \
    5, 1253f0788) + f0
  ffffffff7a11f220 GetResources (1253f06f0, 1253f06f0, \
    ffffffffffffff67, 0, ffffffff7a264db0, 28) + e4c
  ffffffff7a11df38 _XtGetResources (146f8c, ffffffff7fffa650, \
    4, 0, ffffffff7fffa39c, ffffffff7fff9d50) + 120
  ffffffff7a11c644 xtCreate (1253f06f0, 0, 10ae0a468, \
    1254c4780, 10cf04f20, ffffffff7fffa650) + 154
  ffffffff7a125bc0 _XtCreateWidget (10db11a78, 10ae0a468, \
    1254c4780, ffffffff7fffa650, 4, 0) + 278
  ffffffff7a125918 XtCreateWidget (10db11a78, 10ae0a468, \
    1254c4780, ffffffff7fffa650, 4, 1) + d0
  ...

주목할 점은 GetResources() 는 memcpy() 를 호출해서 주어진 주소에서 그 자체로 8 바이트를 카피 합니다!

썬의 X/Motif 개발자에게 이러한 비효율성에 대해 이미 보고를 했습니다.

적어도 솔라리스에서는 error-dump 기능을 fork(2) 에서 pstack(1) 으로 변경하기

현재 error-dump 는 Dmalloc 이 fork() 를 호출 하도록 해서 코어를 덤프 하고 계속 해서 실행되도록 합니다. 저의 테스트에서 이것은 재귀적인 동작을 유발했고 결국 크래쉬로 이끌었습니다. 또한 솔라리스10 에 AppCrash 가 인스톨 되어 있었기 때문에 이것은 끝없이 AppCrash 리포트를 생성해 냈습니다.

대신 스택 트레이스를 생성해서 프로그램의 어느부분에서 에러가 발생되는지 알려 주는것이 훨씬 유용할 것으로 생각됩니다. 이러한 이유로 필자는Dmalloc 의 dmalloc_error() 루틴 (error.c 파일) 의 fork() 를 다음과 같이 변경했습니다:

  char buf[128];
  sprintf(buf, "LD_PRELOAD_64= /bin/pstack %d", (int)getpid());
  system(buf);

"LD_PRELOAD_64= " 커맨드는 pstack(1) 을 위한 Dmalloc 라이브러리의 재귀적인 preloading 을 막아 줍니다.

Dmalloc 은 "리턴 주소"malloc() 과 다른 함수들을 호출한 곳의 "리턴 주소" 를 결정합니다. return.h 파일에 GET_RET_ADDR() 매크로를 보시기 바랍니다. 그러니 이 코드는 진부하고 비효율적(적어도 솔라리스와 썬 스튜디오 플랫폼에서는) 입니다. 이것이 왜 Dmalloc 이 다음과 같이 에러 메세지에서 unknown 이라고 표현 하는 이유입니다:

pointer '0x10d0bf910' from 'unknown' prev access 'unknown'

pstack(1) 기술은 return.h 의 어셈블리-레벨 핵 보다 훨씬 안정적입니다.

만약 dmalloc.h 포함시켜서 어플리케이션을 재컴파일 했다면 Dmalloc 은 호출자의 주소를 Dmalloc 함수들을 통해서 얻을 수 있을 것입니다. 이것은 제 경우에 실용적이지 않았기 때문에 테스트해 보지 않았습니다.

결론

Dmalloc 은 C, C++, Fortran 개발자들에게 매우 가치있는 디버깅 툴로 다른 사용 가능한 디버깅 툴들을 보조해 줍니다. 특히 다른 툴들이 제대로 다룰 수 없는 대용량 어플리케이션에서 매우 유용합니다.

상대적으로 약간의 조정 만으로도 Dmalloc 은 매우 유용해 질 수 있고 특히 썬 스튜디오 컴파일러와 툴을 이용해서 솔라리스 어플리케이션을 개발하는 개발자들에개 매우 유용합니다.

참고자료

 

"개발자코너" 카테고리의 다른 글

2007/06/13 10:57 2007/06/13 10:57

TRACKBACK :: http://blog.sdnkorea.com/blog/trackback/391

댓글을 달아 주세요

  1. 정대하  수정/삭제  댓글쓰기

    솔라리스 사용자로서 관심을 가져야 할 부분이네요.
    저희 회사도 솔라리스를 사용하고 있는데 현재는 개발된 어플리케이션이
    Pro-c로 되어 있습니다.

    2007/09/10 10:41
  2. 이기범  수정/삭제  댓글쓰기

    DTrace도 상당히 관심가는 툴입니다. 그런데, 저희는 어플리케이션의 수행속도를 매우 중시하는 시스템이라 C만 사용하지요. 언제쯤이면 Java가 C를 따라올지 능 궁금증을 가집니다. OpenMP도 일반적인 어플리케이션 개발에 있어서는 매우 유용한 툴입니다. 전체 연산 성능을 향상시키는 데에는 매우 도움이 됩니다. 아직 DTrace상에서 OpenMP의 추적을 해 보지 않았는데, 유용한 도움이 되는 글이라 판단됩니다.

    2007/09/12 14:17
  3. 고진구  수정/삭제  댓글쓰기

    짬짬이 좋은 공부 하고 갑니다. 좋은 자료 많이 많이 올려주세요.

    2007/09/12 22:01
  4. 권미자  수정/삭제  댓글쓰기

    하나하나 알아가는재미가 점점 생기네요..

    2007/09/15 19:05
  5. 박정숙  수정/삭제  댓글쓰기

    좋은 정보 감사해요~

    2007/09/19 03:27
  6. 김광현  수정/삭제  댓글쓰기

    공부을 열심히해야 될것같습니다.
    좋은 내용 항상 감사합니다.

    2007/09/19 14:53
[로그인][오픈아이디란?]

◀ Prev 1  ... 249 250 251 252 253 254 255 256 257  ... 624  Next ▶