| Darryl Gove, 컴파일러 성능 엔지니어링 팀, 2007년 1월 |
|
SHADE 라이브러리는 SPARC 하드웨어를 위한 에뮬레이터 입니다. 에뮬레이터란 프로세서 "인척" 기능하는 소프트웨어를 뜻하며, 에뮬레이터에서 실행 되는 어플리케이션은 실제 하드웨어에서도 실행이 가능 합니다. 에뮬레이터는 기존의 하드웨어가 더이상 사용이 불가능한 상황이나, 어플리케이션의 실행을 위해 기존의 하드웨어와 다른 프로세서를 탑재한 새로운 하드웨어가 필요할 경우, 혹은 타겟 하드웨어가 개발 플랫폼과 다를 때 자주 사용 됩니다.
SHADE 를 사용함으로써 얻을 수 있는 장점은 바로 어플리케이션이 에뮬레이트 되는 동안에 정보를 수집할 수 있는 분석툴을 작성할 수 있다는 것입니다. SHADE 라이브러리는 명령어가 실행된 횟수 혹은 각 명령어 타입들이 얼마나 자주 실행되었는지 등을 추적할 수 있는 몇가지 예제 분석툴들을 함께 제공합니다. 좀더 발전된 분석툴은 어플리케이션이 주어진 캐쉬 구조에서 캐쉬 미스가 얼마나 자주 발생되는지 확인 할 수도 있을 것입니다.
SHADE 얻기
shade-32/bin |
include 파일들은 inc 디렉토리안에, 그리고 라이브러리 파일들은 lib 디렉토리에 있습니다. 몇가지 예제들이 eg 디렉토리에 있으며, 문서는 doc 디렉토리에서 제공됩니다.
이 글에서 코드는 /export/home/shade-32/ 에 설치되어 있다고 가정합니다.
SHADE 아키텍쳐
SHADE는 eg 서브디렉토리에 있는 몇가지 분석기의 소스를 제공합니다. 가장 단순한 분석기는 명령어 카운트를 제공하고 좀더 복잡한 것은 캐쉬 시뮬레이터를 제공합니다. SHADE 분석기의 가장 핵심은 어플리케이션 트레이스의 기록을 순환하여 처리하는 간단한 루프로 되어 있습니다. 이 루프는 각각의 기록을 취해서 필요한 처리 과정을 하게 됩니다.
내부적으로 SHADE는 어플리케이션을 매우 작은 코드 부분으로 나누어서 작동합니다. 이러한 부분들은 두가지의 일을 하는데 그 첫째는 기존의 어플리케이션의 작동과 동일한 작동이며, 둘째는 코드를 실행할 때 발생하는 일들을 트레이스 기록에 기록하는 것입니다.
SHADE 분석기 작성하기
-
shadeuser_initialize함수는 분석되는 프로그램이 로드 되기 전에 호출됩니다. 이것은 분석기에 어떠한 명령어들이 추적되어야 하는지 그리고 어떠한 데이타 구조를 셋업해야 하는지에 대해 미리 알려주는 일을 합니다. -
shadeuser_analyze함수는 어플리케이션의 추적이 종료될때 까지 반복적으로 호출됩니다. 매번 이 함수가 호출될때 마다 SHADE에게 다음에 추적할 명령어들의 셋을 얻어 오도록 해야 합니다. -
shadeuser_report루틴은 어플리케이션의 추적이 종료되기 전에 호출되거나, 리포트가 수신되었다고 시그널을 받았을때 호출됩니다. -
shadeuser_terminate루틴은 어플리케이션의 추적이 종료된다음 호출되며 분석기가 필요한 정리 작업을 할 수 있도록 해 줍니다. -
shadeuser_analusage루틴은 분석기를 위한 사용방법 정보를 돌려 줍니다. -
shadeuser_analversion문자열은 분석기를 구분하기 위해 정의되어야 합니다.
분석기를 작성하는 기본
어플리케이션을 추적하는 것과 직접적인 관계가 없는 두가지 함수가 제공되어야 하는데 하나는 문자열 shadeuser_analversion 에 분석기 이름을 지정하는 것이고 다른 하나는 분석기의 사용 방법을 출력해 주는 루틴(shadeuser_analversion) 을 작성하는 것입니다. 이번 경우에 우리는 분석기 'trace' 를 호출할 것이고 어떠한 사용자 설정 값도 없습니다.
const char shadeuser_analversion[] = "trace "; |
무엇을 추적할 것인지 선택
어떠한 데이타를 수집할지 정의하는 것에는 두가지 단계가 있습니다.
첫번째 단계는 추적 레코드 타입을 정의하여 정보를 저장하는데 사용하는 것입니다. 이번 경우에 우리는 SHADE_SPARCV9_TRACE 레코드 타입을 사용했습니다. 이 타입은 shade_sparcv9.h 헤더 파일에 정의되어 있으며 추적된 결과를 출력하기 위한 명령어 텍스트를 포함합니다. shade_sparcv9.h 헤더 파일에 정의된 또 다른 추적 레코드 타입에는 소스와 데스티네이션 레지스터의 가치에 대한 정보를 포함하는 것도 있습니다. 또한 PC의 명령어 혹은 load, store 등의 명령에 쓰이는 주소들의 유효 주소(Effective Address)들을 추적하기 위한 공간을 가지고 있는 비교적 단순한 SHADE_TRACE 추적 레코드도 있습니다.
/* |
두번째 단계는 SHADE 를 설정해서 오직 분석기에게 필요한 정보만을 전송 하도록 하는 것입니다.
int shadeuser_initialize( |
shade_setopt 호출은 SHADE 라이브러리가 어떻게 fork 와 exec 를 다루어야 하는지 알려 줍니다. SHADE_OPT_FORKNOTIFY 설정은 추적되고 있는 프로그램이 fork할 때 SHADE 분석기에게 통지해줍니다. 그리고 SHADE는 또한 추적되고 있는 프로그램에 의해 fork된 프로세스를 추적하기 위한 새로운 프로세스를 fork 합니다.
SHADE 에게 shade_trsize 을 이용해서 추적 레코드의 크기를 알려 주는 것이 필요 합니다.변수 shade_trace_t 는 이전에 정의 했었던 struct shade_trace_s 와 동일한 것입니다.
다음 단계는 어떠한 명령어들을 추적할지 결정하고 그 명령어들로 인하여 어떠한 것들이 저장할지를 결정하는 것입니다.
먼저, 어떠한 명령어들을 추적할 지 선택하기 위해 shade_iset_newclass 루틴이 호출됩니다. 루틴은 추적할 명령어들의 타입 리스트들을 받습니다. 리스트는 값 -1 에 의해 종료 됩니다. 이 프로그램의 목적을 위해 SHADE_ICLASS_ANY 지정자를 이용하여 모든 명령어들을 선택하는 것이 필요 합니다. 그러나, 다른 분석기는 단지 load 와 store 만을 추적하거나, 다른 명령어들의 셋을 선택할 수 있 다는 것을 알아두시기 바랍니다.
shade_tset_new 루틴은 트레이스 레코드에 저장될 데이타를 선택하는데 사용 됩니다. 다시한번 말해서 루틴은 저장할 데이타들의 리스트를 받고 리스트는 값 -1로 끝냅니다. 실행된 명령어들을 추적하고자 하는 목적을 위해서는 실행된 명령어(SHADE_SPARCV9_TRCTL_I)와 명령어의 주소값(SHADE_TRCTL_PC)을 저장해야 합니다. 다른 지정자를 이용하면 메모리 작업의 유효 주소들 같은 다른 종류의 데이타를 저장할 수 있습니다.
마지막으로 shade_trctl 인터페이스는 SHADE의 결정을 알려 주기 위해 사용됩니다. 이 인터페이스는 역시 명령어들이 단순히 실행될 때나 단순히 종료될 때, 또는 실행되고 종료됐을때 등 언제 추적해야 하는 건지를 결정하는 지정자를 요구 합니다. 지정자 SHADE_TRI_ISSUED 는 SHADE에게 명령어가 실행 혹은 종료 될 때 모두 추적하라고 알려 줍니다.
프로그램 실행 분석
루틴 shadeuser_analyze 은 SHADE 라이브러리에게 반복적으로 호출 되어서 실행중인 어플리케이션의 명령어들을 좀 더 가져오도록 합니다. 이 루틴은 최신의 추적 레코드를 가져 오기 위해 shade_run 루틴을 호출해야 합니다. 그리고 이러한 레코드들을 처리해야 합니다. 이 루틴의 가장 간단한 포맷은 단순히 명령어가 몇번 실행됐는지 숫자를 세는 것입니다. 이러한 일을 하는 예제는 아래와 같습니다.
int shadeuser_analyze(void) |
루틴은 share_run SHADE 루틴을 호출함으로써 채워 지는 지역 배열 레코드들을 정의 합니다. 이 루틴은 배열에 저장된 레코드의 기록을 결과 값으로 돌려 주게 됩니다.
사실, fork 된 추적 프로그램을 다루는 방법은 이것보다 약간 더 복잡합니다. fork 가 일어나면 변수의 합은 새로운 환경으로 복사될 것입니다. 그러므로 새로운 프로세스에서는 이 값을 리셋 하는 것이 필요합니다. 그러나 부모 프로세서에서가 아님을 명심하시기 바랍니다. 이러한 일을 하는 예제는 SHADE와 같이 배포 되는 icount.c 파일을 참고 하시기 바랍니다.
분석기를 작성하는 목적은 추적된 명령어들을 출력하기 위해서 입니다. 이러한 루틴들은 SPIX 라이브러리 안에 (SHADE의 일부로 배포됨) 있어서 트레이스 레코드에 저장된 정보들을 디스어셈블해 줍니다.
int shadeuser_analyze(void) |
루틴 spix_sparc_iop 는 주어진 어셈블리 명령어의 opcode를 돌려 줍니다. 이것은 루틴 spix_sparc_dis32 에 명령어와 주소가 같이 들어갑니다. 이 루틴은 명령어의 디스어셈블된 명령을 버퍼에 저장합니다. 만약 명령어가 성공적으로 디스어셈블 되었다면 결과가 출력 됩니다.
실행 후 정리 작업
추적하려는 어플리케이션이 완전히 종료 하거나 SHADE에 의해 시그널을 받았을때 두가지 루틴 shadeuser_report 가 호출됩니다. 이 루틴은 지금까지 작성된 결과 값의 리포트를 생성해야 합니다. 이번의 특수한 경우 실행 중간에 출력이 생성되었기 때문에 어떠한 레포트도 생성되지 않습니다. 어플리케이션 종료 후의 정리 작업은 shadeuser_terminate 루틴에서 수행됩니다. 이 루틴의 리턴 값은 SHADE 분석기의 리턴 값으로써 사용 됩니다.
추적 툴 빌드하기
예제의 완벽한 코드는 이 글의 마지막에 있습니다. 구축하는 방법은 다음과 같습니다:
$ cc -I/export/home/shade-32/inc -xO2 -DSPIX_ADDR=64 -o trace \ |
추적툴의 출력
분석기가 생성하는 출력값입니다:
% trace -- ls |
앞에서 말한대로 분석기에는 약점이 있습니다. 퍼포먼스 문제 때문에 분석기는 SHADE 라이브러리로 부터 레코드를 블럭 단위로 받습니다 .만약 어플리케이션이 비정상 종료 된다면 SHADE는 모든 명령어들을 모두 기록하지 못한채 리턴될 수 있습니다. 만약 오류의 순간까지 추적을 원한다면 분석기를 수정해서 한번에 오직 하나의 레코드만을 리턴하도록 할 수 있습니다.
결론
프로그램의 실행을 추적하는 것은 상대적으로 쉬운 일임에도, 명령어가 실행되는 정확한 순서를 우리가 볼 수 있도록 해줍니다. 또한 SHADE 라이브러리를 통해 load, store 명령어들의 유효 주소값 혹은 메모리에서 읽혀지거나 저장되는 데이타까지 읽어들이는 것이 가능합니다.
여기 예제의 완벽한 소스가 있습니다:
#define SPIX_ADDR 64 |
"개발자코너" 카테고리의 다른 글
- C++ ABI의 안정성: 프로그래밍 언어의 진화 (댓글 1개 / 트랙백 0개) 2006/02/23
- DTrace를 사용하여 유저가 조정하는 애플리케이션 크래쉬 데이타 정보 모으기 (댓글 1개 / 트랙백 0개) 2006/08/23
- 솔라리스 상에서 "자바 GNOME" 바인딩을 이용해서 개발하기 (댓글 3개 / 트랙백 0개) 2007/04/20
- libumem 라이브러리를 이용하여 어플리케이션 내의 메모리 관리 버그를 잡아 내는 방법 (댓글 1개 / 트랙백 1개) 2006/03/23
- 솔라리스 디바이스 드라이버 작성을 원하십니까? (댓글 0개 / 트랙백 0개) 2008/09/18
- dbx가 프로세스나 코어 파일을 읽지 못하는 이유 (댓글 1개 / 트랙백 0개) 2006/01/23
- dbx를 통한 Java 어플리케이션 디버깅: Java 코드를 위한 업계 최고 수준의 디버깅 (댓글 1개 / 트랙백 0개) 2005/11/23
- OpenMP 소개: 포터블한, 공유 메모리 멀티프로세서 환경에서의 병렬 프로그래밍 API (댓글 10개 / 트랙백 1개) 2007/07/16
- SPOT을 이용한 손쉬운 어플리케이션 성능 분석 (댓글 1개 / 트랙백 0개) 2006/06/23
- Dmake 를 사용하여 어플리케이션 빌드의 속도를 향상시키기 (댓글 0개 / 트랙백 0개) 2008/10/15
댓글을 달아 주세요
예제의 완벽한 코드와 구축하는 방법까지!
2007/09/18 14:42좋은 정보 감사합니다^^
좋은 정보 감사해요~
2007/09/19 04:01유용한 정보 잘 봤습니다. 이젠 에러 잡아봐야겠어요
2007/09/19 16:08