|
개요: 이 문서에서는 솔라리스 OS에서 애플리케이션이 크래쉬 되었을 때 자동적으로 분석/디버깅 정보를 모으는 AppCrash 라는 툴에 대해 소개합니다.
AppCrash는 솔라리스 시스템상에서 어떤 애플리케이션이든 크래쉬 됐을때 자동적으로 분석/디버깅 자료를 모아주는 툴입니다. 이 툴은 애플리케이션 혹은 운영체제에 별다른 변경없이 사용할 수 있으며, 솔라리스 10에서 처음 소개된 장치인 DTrace 기반으로 만들어 졌습니다.
AppCrash 는 애플리케이션에 대한 고객 지원, 품질 관리(QA), 기술 지원 등을 위해 필요한 정보를 얻는 시간을 줄여 줌으로써 소프트웨어 결함에 의해 발생하는 비용을 획기적으로 줄일 수 있습니다. 이 툴은 특히 게릴라 적으로 발생하거나 재연하기 힘든 애플리케이션 오류의 특정 부분을 추적하는데 크게 도움을 줍니다. AppCrash 는 애플리케이션 크래쉬가 자주 발생하는 곳에 대한 오류 리포트와 통계 분석을 할 수 있는 사후처리 툴을 만드는것을 가능하도록 해 줍니다. 또한 AppCrash 덕분에 각각의 문제점을 효과적으로 디버깅하는 방법을 알려주는 툴도 사용가능하게 됩니다.
AppCrash 방법을 다른 것과 구분되게 하는 가장 중요한 점은 애플리케이션 개발자와 시스템 관리자를 포함한 사용자가 정확하게 어떤 데이타를 수집할지, 어떻게 데이타를 처리할지 정할 수 있다는 것입니다. 이에따라 사용자들의 보안/프라이버시 요구 사항을 위반하지 않고도 필요한 데이타를 수집할 수 있게됩니다.
참고로 이 문서에서 말하는 애플리케이션 은 솔라리스 커널 이외의 모든 것입니다. 여기에는 모질라, 스타 오피스, 다양한 부분의 GNOME, OpenGL 그래픽, 애플리케이션 서버 등의 미들웨어를 포함합니다.
이 문서는 독립 소프트웨어 벤더에서 일하는 소프트웨어 개발자 혹은 시스템 관리자, 솔라리스 애플리케이션을 사용하는 엔드 유저를 위해 작성되었습니다.
예를들어 많은 사용자들이 다양한 멀티 프로세스와 멀티티어 애플리케이션을 구동시키고 있는 대형 장치를 생각해 봅시다. 애플리케이션이 크래쉬됐을 때 제일 먼저 어떠한 작업을 해야 할까요?
가장 최선의 상황은 엔드 유저가 문제를 야기한 특정한 입력 혹은 상황을 인식해서 애플리케이션 개발 팀이 재빠르게 문제를 재현하고 고치는 것입니다.
그러나 운나쁘게도 유저가 오직 몇가지 입력/환경 상황만을 정확하게 기억하고, 다른 상황은 왜곡하기도 하며, 문제 상황을 전혀 표현하지 못하는 경우도 있습니다. 디버깅 절차를 시작하기 위해 우선적으로 소프트웨어 개발자가 알아내야할 몇가지 기본적인 필요 사항들이 있습니다. 이런 사항들에는 실패한 애플리케이션의 트레이스백(백트레이스 혹은 스택 트레이스라고 불리기도 함), 메모리맵, 프로세스 내에 활성화된 환경 변수들, OS 버전, 설치된 패치, 사용 가능한 스왑 공간 등이 포함되어 있습니다. 실패상황 때마다 수동으로 이러한 정보를 모으는 것은 매우 비용 및 시간 소요가 크고 에러를 유발 시킬 수 있으며, 몇몇 경우에는 지나치게 힘든 일이 될 수도 있습니다.몇몇 시스템에는 자동 혹은 반자동적인 해결 방법이 존재합니다(아래의 다른 시스템에서의 해결 방법 참조) 그러나 이러한 경우, 얻어진 정보가 애플리케이션과 시스템 벤더들에게 전달되는 동안 사용자들을 그 정보들에 대한 권한을 거의 행사할 수 없기 때문에 보안 문제를 야기할 수 있습니다. 이에 대한 예제는 참고자료 [1] 을 참고 하시기 바랍니다.
코어 파일: 기존의 UNIX 시스템에서의 실패 분석 방법은 애플리케이션이 크래쉬 됐을때 당시의 코어 파일을 모으고 분석하는 것에 기반을 두고 있었습니다. 애플케이션이 개발/사용되는 방법은 UNIX가 처음 개발된 1970년이래 엄청나게 변화되어 왔습니다. 일반적인 애플리케이션 사이즈와 메모리 요구량도 엄청나게 커졌습니다. 또한 더이상 애플리케이션 개발자와 사용자가 같은 사람이 아닙니다.(많은 엔드 유저들은 디버거가 무엇인지 혹은 어떻게 이용하는 것인지 알지 못하는 경우가 많습니다) 인터넷의 발전으로 인해 보안과 프라이버시는 매우 심각한 위협이 되고 있습니다.
현재, 코어 파일을 사용하는 방법은 다음과 같은 몇가지 문제로 인해 위협받고 있습니다.
- 보안/프라이버시 - 보안, 프라이버시 위협은 이전 어느때보다 더욱 중요해졌습니다. 코어 파일은 아래의 항목과 같이 사용자 사이트 밖으로 내보내져서는 안될 데이타를 포함하기도 합니다. (자세한 사항은 참고자료 [1], [2], [3]을 참조)
- 비밀로 분류된 데이타
- 지적재산권이 있는 데이타
- 개인적인(금융 정보 같은) 데이타
- 사이즈 - 코어 파일은 매우 커질 수 있습니다(기가바이트 단위로), 결과적으로 다음과 같은 현상이 야기될 수 있습니다.
- 덤프하는데 시간이 오래 걸리고 시스템 성능에 악영향을 끼칩니다 (코어 파일은 커널 우선순위하에 덤프 되고 다른 모든 것들을 멈추게 함)
- 디스크 용량을 꽉차게하여 오류를 유발 시킬 수 있음
- 사이즈가 너무 커서 벤더에게 보내기 어려움 (비용, 시간, 불편성)
- 기술적인 어려움
- 코어 파일을 벤더에게 보내는 대신 엔드 유저가 그자리에서 분석을 수행 하는 대안이 있을 수 있음. 그러나 이 것은 사용자에게 추가적인 부담(시간과 노력)을 안겨 주고 디버깅 프로세스 속도를 저하 시킬 수 있음. 또한 몇몇 사용자는 이러한 분석을 수행하지 못하거나 수행하지 않음.
- 로컬 분석은 디버거가 요구 되고 또한 그것을 사용할 수 있는 능력(비용, 시간, 불편성)이 요구됨.
- 애플리케이션-레벨 디버거 (
dbx같은) 는 수행중인 프로그램을 디버깅 하는 것에 비해 엄청난 제약 사항이 있음. - 유저 사이트의 애플리케이션 바이너리가 종종 완전 최적화 되있고, 디버깅이 불가능하고 혹은 스트립(strip)이 되있기 때문에
dbx의 기능이 좀 더 제약 됨. 결과적으로 대부분의 경우 ISV는 트레이스백 혹은 메모리 맵 같은 것에 비해 코어 파일에서 많은 정보를 얻을 수 없고 이러한 정보들은 사실 코어 덤프 없이도 얻을 수 있음. - 대부븐의 애플리케이션 개발자는 하드웨어-특수한 어셈블리 레벨의 디버깅을 할 수 없거나 하지 않습니다. 그러므로 코어 파일이 추가적인 디버깅 정보를 포함 하고 있더라도 ISV는 대부분의 경우 실제로 그것을 이용할 수 없습니다.
- 코어 파일이 생성된 엔드유저 머신에서 디버깅을 위해 다른 ISV의 머신으로 카피 할 경우 많은 특수한 문제들이 발생 합니다. 추가적인 정보는
dbx에서help core mismatch커맨드를 입력해 보시기 바랍니다; 또한 참조 [4]를 보시기 바랍니다. 이러한 문제들 때문에 애플리케이션 코어 덤프는 종종 다른 컴퓨터에서 전혀 사용 할 수 없는 경우가 생깁니다. 이러한 문제는 조금씩 완화되고 있지만 아직까지 존재하는 문제 입니다.
- 코어 파일이 사용가능하지 않음 (위의 모든 경우의 결과로)
- 많은 애플리케이션이 동시에 코어 덤프를 생성하는 것을 막음 (시그널 핸들러를 설치 하거나
setrlimit(RLIMIT_CORE, &zero)을 호출), 그렇게 함으로써 분석을 위한 코어 파일이 사용가능하지 않습니다. 또한 몇몇 유저 사이트는 코어 파일 생성 자체를 아예 막아 버리거나(유저 제한 값에 0을 지정 하거나coreadm(1)커맨드 이용) 혹은 그들의 유저 제한 값을 아주 제한적으로 설정 함으로써 코어 파일이 저장 되지 못하도록 합니다.
- 많은 애플리케이션이 동시에 코어 덤프를 생성하는 것을 막음 (시그널 핸들러를 설치 하거나
간섭(Interpose) 라이브러리: 애플리케이션 크래쉬에 대응하는 또다른 기술적인 접근에는 고유의 SIGBUS 와 SIGSEGV 시그널 핸들러를 설치하는 간섭 라이브러리를 구현하는 것이 있습니다.(즉 에러 보고를 위한 상태 정보를 저장할 것입니다). 이에 대한 예제는 참고자료 [5] 를 참고 바랍니다. 그러나 다음과 같은 이유로 이를 구현하는 것은 쉬운 일이 아닙니다.
- 라이브러리는 반드시 애플리케이션의 정상적인 시그널 핸들링 작업을 방해해서는 안됩니다.
- 각 애플리케이션의 실행은 반드시 이 라이브러리를 삽입하기 위해 환경값에 변화를 주게됩니다. 이는 모든 유저로하여금 환경을 바꾸도록하거나, 관찰되고있는 각각의 애플리케이션 실행시에 래퍼(Wrapper) 스크립트를 추가함으로써 할 수 있습니다. 그러나 이 두가지 방법은 모두 많은 수의 유저 혹은 많은 수의 애플리케이션이 있는 시스템에서는 매우 불편한 방법입니다.
- 라이브러리 삽입자를 만드는 것은 컴파일러와 특정 수준의 프로그래밍 스킬을 요구합니다. 많은 사용자들에게는 좀 더 쉬운 해결 방법이 필요합니다.
- 일반적으로 라이브러리를 삽입하는 것은 디버깅, 퍼포먼스 튜닝의 기술이므로 실제 제작과정에서는 추천되지 않습니다. 제작과정 내내 사용 할 수 있는 새로운 방법이 필요 합니다.
애플리케이션 시그널 핸들러: 물론 각 애플리케이션에 SIGBUS 와 SIGSEGV 시그널 핸들러를 설치 하는 것이 가능하고 가치도 있습니다. 그러나 이러한 접근 방법은 ISV 들에게 그들의 모든 애플리케이션을 수정 하도록 요구하게됩니다. 만약 ISV 가 그렇게 하더라도 크래쉬 상황을 처리 하는 것은 여전히 쉬운 방법이 아닙니다. 또한 시그널 핸들러를 프로그래밍 하는 것은 까다롭고 플랫폼 종속적인 방법입니다. 예를 들어 시그널 핸들러는 Async-Signal-Safe 하지 않은 루틴을 호출해서는 안됩니다. (Async-Signal-Safe 의 정의는, attributes(5) 맨페이지를 참고 바랍니다.)
몇몇 ISV들은 여러해 동안 이러한 시그널을 처리해 왔고 시그널 핸들러가 작동하는 방식대로 작업을 수행했습니다. 그러나 이러한 접근 방법은 대부분의 애플리케이션에 적용되지는 않습니다. 우리는 광범위한 시스템 상에서 애플리케이션의 수정 없이 애플리케이션 크래쉬를 다룰 수 있는 방법이 필요합니다.
truss(1): 한가지 더 흥미로운 해결 방법은 truss 를 사용하여 애플리케이션의 시그널을 모니터링하는 방법입니다. truss는 시스템과 라이브러리 호출을 추적하는 일반적인 기능 대신 조용히 프로세스를 감시하고 특별한 시그널 셋을 기다리는 기능을 가지고 있습니다.
예를 들어 다음은 truss 가 SIGSEGV 와 SIGBUS 를 감시하는 상태에서 애플리케이션을 백그라운드로 실행시키는 절차입니다. 만약 시그널이 발생 한다면 truss 는 애플리케이션을 STOPPED 상태로 남겨 둡니다. kill 커맨드가 프로세스가여전히 존재한다는 것을 발견하면 /proc 유틸리티 pmap(1) 과 pstack(1)을 사용해서 프로세스의 메모리 맵과 트레이스 백을 저장합니다. 최종적으로 prun(1) 커맨드를 통해 프로세스는 재시작되거나 종료될 수 있습니다.
application_invocation &
pid=$!
truss -t \!all -m\!all -s \!all -S segv,bus -p $pid
if kill -0 $pid ; then
pmap $pid
pstack $pid
prun $pid
fi
truss(1) 솔루션의 최대 문제점은 오직 하나의 프로세스만을 처리 할 수 밖에 없다는 것입니다. (많은 애플리케이션이 프로세스의 하이어라키를 만든다고하더라도) 또한 '라이브러리 삽입 기술'과 같이 각 애플리케이션이 각자의 맞게 수정된 실행 스크립트를 가져야 합니다.
이글에서 제안하고자하는 해결 방법은 솔라리스의 새로운 기능인 DTrace,를 이용해서 애플리케이션 크래쉬를 감시하고 각각의 크래쉬에 대해 유저에게 제공되는 스크립트를 이용해서 처리 하는 것입니다.
DTrace는 솔라리스 10의 커널과 애플리케이션 퍼포먼스 튜닝 및 디버깅을 위해 소개된 강력한 신기능 입니다. (참고자료 [6] 과 dtrace(1M) 맨페이지를 참고 바람). 커널, 애플리케이션 코드를 동적으로 다루므로 솔라리스 커널 레벨로 광범위한 시스템에 사용 가능합니다. 동적으로 삽입된 probe는 비활성화 됐을때에는 어떠한 오버헤드도 주지 않으며 실제 제작 시스템에서 안전하게 사용할 수 있습니다.
이 방법을 구현에는 다음의 DTrace 스크립트가 포함되어 있습니다. (주의: .txt 파일 확장자를 빼고 저장하기 바람.)
app_crash.d
이것은 다음의 템플릿과 같은 사용자 정의 쉘 스크립트와 혼합되어 있습니다. (주의: .txt 파일 확장자를 빼고 저장하기 바람.)
runme_on_app_crash
AppCrash 를 이용하여 각 개인 유저는 각자의 애플리케이션을 모니터링 할 수 있으며, 시스템 관리자는 다른 모든 유저들이 실행하고 있는 애플리케이션을 감시할 수 있습니다. 이 작업을 위해서 관리자는 부팅시에 RC 스크립트 메카니즘을 이용해서 데몬 형식으로 실행 시킬 수 있습니다. (예를 들어 /etc/rc2.d/S97app_crash를 생성 ) 혹은 솔라리스 서비스 관리 설비(SMF)를 이용할 수 도 있습니다 [7]. 주의: 잘 동작하는 솔라리스 데몬을 만들기 위해서는 app_crash.d 를 nohup(1) 으로 실행하는 것이 좋은 생각일 수도 있습니다. 또한 Perl 스크립트는 다음의 참고자료 [8] 대로 데몬을 만드는데 이용될 수 있습니다.
app_crash.d 데몬이 수행되게 되면 DTrace는 SIGSEGV 혹은 SIGBUS 시그널을 발생시키는 어떠한 애플리케이션에 대해서도 반응 하게 됩니다. 만약 프로세스가 환경변수 $ON_APP_CRASH_INVOKE 에 유저-컨트롤 쉘 스크립트의 경로를 지정한다면 (runme_on_app_crash 같은), 그러면 DTrace 스크립트는 다음과 같은 일을 할 것입니다:
SIGSEGV혹은SIGBUS시그널이 발생 하면 프로세스를 정지.- 유저가 정의한 쉘 스크립트(
runme_on_app_crash같은)를 수행시켜서 디버깅에 필요한 모든 정보를 수집. - 정상적인 처리 작업을 다시 시작함. 예를 들어 만약 코어 덤프를 생성하도록 설정되어 있다면 그 일을 수행하게 될 것임.
설명한데로 환경변수 $ON_APP_CRASH_INVOKE 에 유저-컨트롤 쉘 스크립트를 지정하는 것은 사용자에게 어떠한 행동도 취할 수 있는 완벽한 컨트롤 권한을 주는 것이라고 할 수 있습니다. 사용방법은 각 애플리케이션에 맞게 수정 될 수 있거나 (애플리케이션 래퍼 스크립트에 설정하여) 유제에 맞게 (유저의 개인 시작 스크립트에 설정하여)도 수정 될 수 있습니다. 물론 이것은 또한 사용자 혹은 시스템 관리자가 스크립트에 의해 수집되는 디버깅 정보에 대한 완벽한 컨트롤을 가지고 어떠한 일이든 할 수 있음을 의미 합니다. 위의 runme_on_app_crash 는 단지 템플릿일 뿐입니다.
예를 들어 어떠한 특정 ISV가 DTrace 스크립트 app_crash.d 를 데몬으로써 실행 시키길 원한다고 합시다. 또한 $ON_APP_CRASH_INVOKE 환경 변수를 애플리케이션의 스타트업 스크립트에 지정했다고 합시다. 이 방법으로 AppCrash 는 오직 그 애플리케이션을 위해서만 사용될 뿐 그 이상 혹은 그 이하도 안될 것입니다.
사용자가 모든 필요한 정보를 수집했다면 그들은 그것을 리뷰해보고 어떠한 민감한 자료도 포함되지 않았는지 확인해 볼 고 그것을 그들의 애플리케이션 벤더에게 보낼 것입니다. 만약 원한다면 이러한 과정을 전부 스크립트를 이용해서 자동적으로 수행 할 수 있습니다. (runme_on_app_crash 쉘 스크립트에 아래쪽에 커멘트를 확인 바랍니다).
알아둘 점은 수집된 정보 그 자체가 문제 해결을 하려는 ISV 혹은 썬 엔지니어에게 적당한 정보가 아닐 수도 있습니다. 이러한 정보 만으로 문제를 해결 할 수 있다고 아무도 장담할 수 없습니다. 제일 좋은 방법은 재현이 가능한 테스크 케이스를 찾아 내고 그것을 소프트웨어 벤더에게 보내는 것입니다. 그렇지 않다면 runme_on_app_crash 같은 스크립트를 통해 수집된 정보는 디버깅 절차를 분명 도와줄 것이고 대부분의 경우 문제를 해결 하는데 충분한 정보가 될 수 있습니다.
만약 사용자가 트레이스백에서 함수의 이름 대신 물음표 마크만을 보고 있다하더라도 상관없이 정보를 ISV에 보낼 수 있습니다. 애플리케이션 소유자는 실제 함수 이름으로 복구 할 수 있을 것입니다. 예를 들어 참고자료 [5] 에 기술된 unstrip_traceback 라고 불리는 툴을 이용 할 수 있을 것입니다.Generating and Handling Application Traceback on Crash. 여기에 향상된 버젼의 Perl 스크립트를 받으 실 수 있습니다. (주의: .txt 확장자를 빼고 저장 바람)
unstrip_traceback
AppCrash에 의해 가능한 것들은:
- ISV는 아마 자동적으로 크래쉬 리포트를 생성할 수 있고 자동 혹은 반자동적인 시스템을 개발하여 어느 구간에서 애플리케이션이 자주 크래쉬 되는지 통계적으로 분석할 수 있을 것입니다. 이 방법은 엄청나게 QA 노력을 도와 줄 것이고 개발자, QA 엔지니어들에게 아주 효과적인 인센티브가 될 수 있는 절차를 제공할 수 있을 것입니다.(예를 들어 소프트웨어의 크래쉬되는 빈도가 낮다면 보너스가 더 커지게 될 것입니다.)
- 룰 기반의 시스템은 또한 크래쉬 리포트를 분석할 수 있고 각 문제 상황이 어떻게하면 효과적으로 디버깅 될 수 있는지에 대한 조언을 제공할 수 도 있습니다. 예를 들어 만약
malloc(3C)혹은free(3C)에 문제가 있다면 메모리에 문제가 있다는 것을 의미 함으로 솔라리스에 기본적으로 내장된watchmalloc(3MALLOC)혹은libumem(3LIB)같은 툴을 이용해 문제를 해결하는 것이 가장 좋은 방법이라고 조언해 줄 수 있습니다. 이러한 룰 기반의 시스템은 시간을 줄요 주고 크래쉬를 분석하는 시간을 줄여 주며 문제 보고를 빨리 할 수 있도록 해 줍니다.
위와 같은 제안을 구현 하는 것은 소프트웨어 결점에 드는 비용을 줄이는데 크게 도움을 줄 것입니다.
주의할점은 여러가지 크래쉬 리포트를 처리하는 어떠한 자동 시스템은 각 리포트가 어떠한 내용을 담고 있고 어떠한 포맷인지에 대한 표준적인 동의가 필요합니다. 이러한 표준은 애플리케이션과 유저 사이트에 따라 달라질 수 있습니다. 사용자와 ISV는 얻어진 정보에 대한 권한을 가지고 있습니다. 오직 필요한 작업은 그것을 재배열 시키는 일 입니다.
app_crash.d 스크립트를 root 에 의해 시스템와이드한 데몬으로 실행 시키는 것이 이러한 방법을 사용하는 한가지 방법이지만 좀더 엄격히 말한다면 이것이 유일한 방법은 아닙니다. 엔드유저는 그 본인이 이 섹션에서 설명되는 대로 이러한 스크립트를 수행 할 수 있는 DTrace 혀용 권한을 가지고 있습니다.
app_crash.d 같은 DTrace 스크립트는 root 혹은 아래의 권한을 가지고 있는 다른 유저에 의해 수행 될 수 있습니다. 아래의 내용은 /etc/user_attr 파일의 내용입니다:
<user-name>::::defaultpriv=basic,dtrace_proc,dtrace_kernel
/etc/user_attr 파일은 root가 소유 하고 있고 오직 root 권한을 가진 시스템 관리자 만이 수정할 수 있습니다. /etc/user_attr 가 수정 되면 사용자는 세로운 설정을 적용시키기 위해 로그오프, 로그온을 하길 요구 됩니다. 이것은 최소 권한 설비가 제공하는 잘 만들어진 프로세스의 액션 컨트롤을 제공하는 기능의 일부 입니다. 좀 더 자세한 정보는 privileges(5) 를 참조 바랍니다. 시스템 관리자는 또한 ppriv(1) 커맨드를 아래와 같이 이용해서 일부 권한을 임시적으로 제공할 수 있습니다\:
# ppriv -s A+dtrace_proc,dtrace_kernel PID
PID는 유저 쉘의 프로세스 ID를 의미 합니다.
주의할 점은 위에서 언급한 DTrace 권한은 DTrace의 모든 설비를 사용 가능하도록 하는 권한입니다.(커널 설비를 포함) 그러므로 이러한 권한들은 적절하게 사용해야 하며 서비스 거부 공격(DoS) 같은 것에 사용 할 수도 있음을 항상 염두에 두어야 합니다.
이글에서 설명된 app_crash.d DTrace 스키립트를 수행하기 위한 권한들은 매우 안전 합니다. 그러나 DTrace 스크립트는 쉽게 만들어 질수 있고 또한 악용 될 수도 있습니다. 만약 일반 유저들이 DTrace 권한을 가짐으로써 야기 되는 위험을 가지길 원치 않는 다면 사용자는 항상 app_crash.d 를 root가 소유하는 데몬의 일부로 실행 시킬 수 있습니다.
DTrace 스크립트 app_crash.d 는 매우 간단하지만 DTrace 스크립팅에 익숙하지 않다면 그 동작에 대해 명확한 이해가 안될지도 모릅니다. 그러므로 app_crash.d 가 하는 일을 줄 단위로 설명 하도록 하겠습니다.
#!/usr/sbin/dtrace -qws
이것은 스크립트가 직접적으로 수행될 수 있고(적절한 실행 권한이 있다고 가정함) /usr/sbin/dtrace 바이너리를 수행함을 의미 합니다. "-q" 는 조용하드는 의미의 quiet (어떠한 추가 메세지도 생성하지 않음)를 의미 합니다. "w" 는 사용자가 destructive actions 이라 불리는 system() 같은 액션을 허용 함을 의미 합니다. "s" 는 다음이 DTrace 스크립트 임을 의미 합니다.
#pragma D option strsize=500이 옵션은 DTrace가 문자열의 길이를 500자 까지 허용함을 의미합니다. 기본 사이즈 256은 우리의 목적에는 맞지 않습니다.
proc:::signal-send
/(args[2] == SIGBUS || args[2] == SIGSEGV) &&
pid == args[1]->pr_pid/
DTrace의 문법에서, 이러한 라인들은 어떠한 프로바이더의 predicate 조건이 참이더라도 procsignal-send를 사용함을 지정합니다. 즉 이 후에 오는 DTrace 코드들은 시스템상의 어떠한 프로세스라도 SIGBUS 혹은 SIGSEGV 을 발생 혹은 전송 시에 무조건 실행 되고 수신 프로세스 (즉 플세스 ID가 args[1]->pr_pid에 저장된 프로세스) 는 전송 프로세스와 같습니다 (pid).
stop();
이것은 별도의 지시가 없을때까지 시그널을 발생한 프로세스를 정지 시킨다는 의미 입니다.
system( "%s=%d; %s=%d; %s=%d; %s=%s; %s %s %s %s %s %s %s %s %s", "CRASH_PID", pid, "CRASH_UID", uid, "DTRACE_UID", $uid, "PROG", execname, "SCRIPT=`/bin/pargs -e $CRASH_PID | ", " /bin/grep ON_APP_CRASH_INVOKE | /bin/cut -d= -f2`;", "[ -z \"$SCRIPT\" -o ! -x \"$SCRIPT\" ] && exit 0;", "if [ $DTRACE_UID -eq 0 -a $CRASH_UID -ne 0 ] ; then", " USER_NAME=`/bin/getent passwd $CRASH_UID|/bin/cut -d: -f1`;", " /bin/su $USER_NAME -c \"$SCRIPT $CRASH_PID $PROG\";", "else ", " $SCRIPT $CRASH_PID $PROG; ", "fi" );
이 긴 라인은 특정한 Bourne 쉘 커맨드를 실행하는 라인 입니다. 우리는 쉘 스크립트 도우미를 써서 읽기 쉽게 만들 수 있지만 이것은 복잡한 설치 과정이 요구 됨으로 여기서는 그냥 한줄 커맨드를 이용하도록 합니다.
DTrace 스크립트에서 system() 액션은 printf()의 매개 변수 처리 같은 기능을 허용 합니다.
위의 스크립트는 다음과 같은 단계를 수행 합니다:
pargs(1)커맨드를 수행 시켜서 크래쉬되는 프로세스로 부터ON_APP_CRASH_INVOKE 환경 변수를 추출합니다.ON_APP_CRASH_INVOKE가 정의 되지 않았는지 ($SCRIPT가 비어 있음) 혹은$SCRIPT가 가르키고 있는 사용자 스크립트가 수행 불가능한지 알아보고 그러할 경우 종료 합니다.app_crash.d스크립트의 소유자가root($uid가 0인) 인지 체크 하고 크래쉬된 프로세스가root에 속해 있지 않은지 체크 합니다. 만약root가app_crash.d를 실행하고 있다면 스크립트는 크래쉬된 프로세스의 소유자의 유저 이름을 추출 합니다./bin/getent passwd <uid> |/bin/cut -d: -f1
그리고
su(1)커맨드를 사용하여 유저-정의된 스크립트를 그 유저의 권한으로 실행 시킵니다:su <user_name> -c <script>
user ID를 결정하는 또 다른 방법은 (예를 들어
$USER를 검색하는 것 같은) 보안 문제를 야기 할 수 있습니다(해커가 그들의$USER환경 변수를root로 지정할 수 있고ON_APP_CRASH_INVOKE를 터미널 에뮬레이터 같은 것을 시작하는 스크립트로 지정해서 root 쉘의 권한을 얻을 수도 있습니다).- 만약
app_crash.d의 소유자가root가 아니라면,su는 사용되지 않고 유저가 정의한 스크립트를 곧바로 실행 합니다. 이 방법을 통해 어떠한 유저라도app_crash.d를 수행 할 수 있습니다. 그러나 오직 그 유저에 의해 소유되고 있는 프로세스들에게만 동작할 것입니다. - 마지막 단계로
app_crash.d가prun(1)커맨드를 통해 정지상태에서 다시 실행됩니다. :system("/bin/prun %d", pid);
유저-정의 스크립트의 예인 runme_on_app_crash 는 다음과 같은 작업을 합니다:
- 프로세스 ID (
$PID) 과 입력 변수로 부터 프로그램 이름을 가져 옵니다. - 시스템 콘솔에 메세지를 보냅니다.(만약 권한이 허용한다면).
- 다음의 솔라리스 커맨드를 크래쉬된 프로세스를 위해 사용 합니다 (
pfiles(1)커맨드는 솔라리스10에 포함된 명령어로 관계경로명을 보여 주는 커맨드 임):/bin/pstack $PID /bin/pmap -x $PID /bin/pldd $PID /bin/ptree $PID /bin/pargs -ace $PID /bin/plimit -m $PID /bin/pwdx $PID /bin/pfiles $PID
- 시스템 설정 데이타를 추출 합니다 (자세한 내용은 스크립트를 참조).
크래쉬된 프로세스를 지정하는 커맨드는 솔라리스의 proc(4) 설비에 기반을 두고 있습니다. 이 것들은 잠재적으로 크래쉬된 프로세스로 부터 유용한 정보를 수집 합니다: 트레이스백, 메모리맵, 라이브러리 의존성, 프로세스 트리, 프로세스 변수와 환경 문자열, 프로세스 제한, 작업 디렉토리, 열고 있는 모든 파일 등. 이러한 정보들은 간단히 말해서 ASCII-텍스트 형태로 아주 쉽게 유저에게 접근이 가능합니다. 이러한 출력은 /var/tmp/appcrash.$PROG.$PID 파일에 그대로 재전송 됩니다. 주의할점은 /var/tmp 에 파일은 재부팅 후에도 지워지지 않을 것이지만 /tmp 는 보통 그렇지 않습니다.
종종 몇몇 애플리케이션은 SIGSEGV 과 SIGBUS 를 애플리케이션 크래슁에 관계 되지 않은 특별한 목적을 위해 사용 하는 경우도 있습니다. 아직 까지 이러한 프로그램을 접해보진 못했지만 그러할 가능성은 충분히 있습니다. 이러한 프로그램들에서 AppCrash는 SIGSEGV/SIGBUS 가 충분히 주어지는 상황에서 /var/tmp 에 많은 수의 파일을 만들고 시스템의 성능을 저하시킬 수 있습니다. 만약 이러한 일이 벌어 진다면 AppCrash 스크립트는 이러한 일상적이지 않은 상황을 위해 예를 들어 특정 애플리케이션의 이름을 빼는것 같은 수정을 가해야 할 필요성이 생길 것입니다. 이러한 작업은 runme_on_app_crash 스크립트의 predicate를 조정 함으로써 할 수 있습니다.
또한 알아둘 점은 AppCrash 의 장점은 그 컴포넌트들이 모두 쉽게 커스터마이즈 할 수 있는 스크립트들이라는데에 있습니다.
다음의 간단한 테스트 프로그램이 버그를 가지고 있다고 합시다. 이 프로그램은 서브루틴 sub2()에서 널 포인터를 참조하고 있습니다:
% cat test1.c
#include <stdio.h>
#include <stdlib.h>
static void sub2(int *p)
{
int i;
i = *p;
}
static void sub(int *p)
{
sub2(p);
}
int main()
{
int *p=NULL;
sub(p);
return 0;
}
% cc -o test1 test1.c
단계 1 app_crash.d 데몬을 시작 시키고 현재 유저가 위에서 설명된 대로 DTrace 를 수행하기 위한 충분한 권한을 가지고 있다고 가정합니다.
% ./app_crash.d & [1] 5707
단계 2 이제 다른 터미널 윈도우에서 필요한 환경 변수들을 정의 합니다:
% setenv ON_APP_CRASH_INVOKE $HOME/tests/runme_on_app_crash
단계 3 test1 프로그램을 ON_APP_CRASH_INVOKE 가 정의된 터미널 윈도우에서 실행 시킵니다:
% test1 Segmentation Fault
크래쉬가 된 후 정보는 쉘 스크립트에 정의 된 /var/tmp directory 에 저장됩니다. (아래의 출력들은 가독성을 위해 약간 편집 되었습니다.):
% ls -lt /var/tmp/ | head -2
total 42
-rw-r--r-- 1 gregns staff
4037 Apr 20 11:30 /var/tmp/appcrash.test1.5174
% cat /var/tmp/appcrash.test1.5174
Output from runme_on_app_crash
Program: test1
Process ID: 5174
Application Debugging Data
--------------------------
> /bin/pstack 5174
5174: test1
08050652 sub2 (0) + 12
08050688 sub (0) + 18
080506bf main (1, 8047cec, 8047cf4) + 1f
080505aa ???????? (1, 8047db0, 0, 8047db6, 8047dc8, 8047e49)
> /bin/pmap -x 5174
5174: test1
Address Kbytes RSS Anon Locked Mode Mapped File
08047000 4 4 4 - rwx-- [ stack ]
08050000 4 4 - - r-x-- test1
08060000 4 4 4 - rwx-- test1
FEEE0000 4 4 4 - rwx-- [ anon ]
FEEF0000 24 12 12 - rwx-- [ anon ]
FEF00000 724 724 - - r-x-- libc.so.1
FEFC5000 24 24 24 - rw--- libc.so.1
FEFCB000 8 8 8 - rw--- libc.so.1
FEFDA000 128 128 - - r-x-- ld.so.1
FEFFA000 4 4 4 - rwx-- ld.so.1
FEFFB000 8 8 8 - rwx-- ld.so.1
-------- ------- ------- ------- -------
total Kb 936 924 68 -
> /bin/pldd 5174
5174: test1
/lib/libc.so.1
> /bin/ptree 5174
225 /usr/lib/inet/inetd start
5139 /usr/sbin/in.rlogind
5141 -csh
5174 test1
> /bin/pargs -ace 5174
5174: test1
argv[0]: test1
envp[0]: HOME=/home/gregns
... [removed more environment variable settings] ...
envp[19]: ON_APP_CRASH_INVOKE=
/home/gregns/tests/runme_on_app_crash
> /bin/plimit -m 5174
5174: test1
resource current maximum
time(seconds) unlimited unlimited
file(mbytes) unlimited unlimited
data(mbytes) unlimited unlimited
stack(mbytes) 10 unlimited
coredump(mbytes) 0 unlimited
nofiles(descriptors) 256 65536
vmemory(mbytes) unlimited unlimited
> /bin/pwdx 5174
5174: /home/gregns/tests
> /bin/pfiles 5174
5174: test1
Current rlimit: 256 file descriptors
0: S_IFCHR mode:0620 dev:270,0 ino:12582924 uid:28715
gid:7 rdev:24,4
O_RDWR
/devices/pseudo/pts@0:4
1: S_IFCHR mode:0620 dev:270,0 ino:12582924 uid:28715
gid:7 rdev:24,4
O_RDWR
/devices/pseudo/pts@0:4
2: S_IFCHR mode:0620 dev:270,0 ino:12582924 uid:28715
gid:7 rdev:24,4
O_RDWR
/devices/pseudo/pts@0:4
System Configuration Data
-------------------------
> /bin/uname -a
SunOS rahova 5.10 Generic i86pc i386 i86pc
> /bin/cat /etc/release
Solaris 10 3/05 s10_74L2a X86
Copyright 2005 Sun Microsystems, Inc.
All Rights Reserved.
Use is subject to license terms.
Assembled 22 January 2005
> /usr/sbin/psrinfo -v
Status of virtual processor 0 as of: 04/20/2005 11:30:49
on-line since 03/30/2005 14:43:48.
The i386 processor operates at 2393 MHz,
and has an i387 compatible floating point processor.
Status of virtual processor 1 as of: 04/20/2005 11:30:49
on-line since 03/30/2005 14:43:53.
The i386 processor operates at 2393 MHz,
and has an i387 compatible floating point processor.
> /usr/sbin/swap -s
total: 62464k bytes allocated + 12248k reserved =
74712k used, 6891300k available
> /usr/sbin/swap -l
swapfile dev swaplo blocks free
/dev/dsk/c1t0d0s1 28,65 8 8389432 8389432
> /usr/sbin/prtconf|/bin/head -2
System Configuration: Sun Microsystems i86pc
Memory size: 3327 Megabytes
> /bin/showrev -p|/bin/cut -d' ' -f2|/bin/sort
116299-08
116303-02
위의 파일은 이제 디버깅을 위해 다른 곧으로 보내질 준비가 되었습니다. 주목할점은 pstack(1) 에 의해 생성된 트레이스백에서 명백하게 sub2() 안에 버그가 있음을 보여줍니다. 또한 그것은 잘못된 루틴을 호출하도록 하는 체인 또한 포함 하고 있습니다.
마이크로소프트 윈도우는 이 분야에서 흥미로운 기능을 가지고 있습니다. 참고자료 [9]의 Windows Error Reporting for Developers 를 참고 바랍니다.
마이크로 소프트 뿐만 아니라 ISV 도 자동적으로 크래쉬 데이타를 수집해 줍니다 (AppCrash 가 하는 일과 같이), 그러나 사실 이러한 에러 보고를 사용자로 부터 수집하고 ISV가 이러한 오류 보고를 마이크로소프트 사이트로 부터 접근 할 수 있도록 합니다.
마이크로소프트는 수집된 데이타를 암호화 하고 오직 의도된 ISV 혹은 마이크로소프트 직원만이 복호화 할 수 있도록 합니다. 이것은 나쁜 생각은 아니지만 사용자 사이트에서 데이타가 빠져 나가기 전에 그 데이타를 사용자가 검사해 볼수 없다는 것에 문제가 있습니다. 또한 어떠한 정보를 수집하는 지에 대한 제어권한도 주어지지 않습니다.
마이크로소프트가 크래쉬 상황에서 데이타를 수집하는 것에 대한 좀 더 자세한 정보는 참고자료 [10], Microsoft Online Crash Analysis Data Collection Policy 을 참고 바랍니다..
마이크로소프트의 미니덤프에 대한 좀 더 자세한 정보는 참고자료 [11], Post-Mortem Debugging Your Application with Minidumps and Visual Studio .NET 을 참고 바랍니다.
우리는 이 글에서 설명한 AppCrash 방법이 좀더 유연하고 사용자와 ISV들에게 자유와 권한을 더 많이 줌을 설명 했습니다. 물론 사용자는 어떠한 접근법을 사용할지 결정 할 수 있습니다.
특수화된 상용 제품과 서비스들이 자동적으로 크래쉬를 모니터링하고 애플리케이션을 분석하는 기능을 제공하기도 합니다. 예를 들어 참고자료 [13] 을 참고 바랍니다.
관련된 토의는 참고자료 [1] 과 [3] 을 참고 바랍니다.
이 글은 솔라리스를 사용하는 ISV, 사용자가 DTrace기반 해결 방법을 이용하여 안전하게 디버깅 정보를 모으는 것에 대한 정보를 제공했고, 소프트웨어 결점의 비용을 줄일 수 있는 품질 향상에 관해서도 도움을 제공 했습니다. 사용자는 이러한 분석 데이타 수집을 자동으로 수행 할 수 있고 만약 원한다면 어떠한 정보를 수집할지에 대한 완전한 권한도 가지고 있습니다.
AppCrash 업데이트와 관련된 토의는 참고자료 [14] 를 참고 바랍니다.
- [1] US Department of Energy: Office XP Error Reporting May Send Sensitive Documents to Microsoft
- [2] Protecting sensitive data in memory, John Viega
- [3] Scrash: A System for Generating Secure Crash Information (pdf), Pete Broadwell, et al.
- [4] dbx and System Libraries: Why Can't dbx Read My Process or Core File?, Chris Quenelle and Ann Rice
- [5] Generating and Handling Application Traceback on Crash, Greg Nakhimovsky
- [6] Solaris Dynamic Tracing Guide
- [7] Solaris Service Management Facility - Quickstart Guide
- [8] Unix Daemons in Perl
- [9] Windows Error Reporting for Developers
- [10] Microsoft Online Crash Analysis Data Collection Policy
- [11] Post-Mortem Debugging Your Application with Minidumps and Visual Studio .NET, Andy Pennell
- [12] Mac OS X CrashReporter
- [13] BugSplat Launches Automated Crash Monitoring and Analysis Service For Applications Deployed at a Software Vendor's Customer Sites
- [14] Solaris AppCrash Updates
Greg Nakhimovsky 와 Morgan Herrington 은 애플리케이션 소프트웨어 벤더들과 협력하여 그들의 제품이 썬 시스템에서 잘 작동하도록 하는 일을 합니다.
"개발자코너" 카테고리의 다른 글
- DTrace를 사용하여 유저가 조정하는 애플리케이션 크래쉬 데이타 정보 모으기 (댓글 1개 / 트랙백 0개) 2006/08/23
- D 스크립트 배우기 (댓글 0개 / 트랙백 0개) 2009/11/23
- C++ 표준 라이브러리인 libCstd와 libstlport 비교하기 (댓글 1개 / 트랙백 0개) 2006/07/23
- 썬 스튜디오: VIS 명령을 사용하여 중요한 루틴의 속도를 향상시키기 (댓글 1개 / 트랙백 0개) 2006/02/23
- 썬 스튜디오 dbx 를 이용한 AMD64 명령어 레벨의 디버깅 (댓글 0개 / 트랙백 0개) 2007/12/14
- 솔라리스에서의 유저 인증: Part 1 (댓글 0개 / 트랙백 0개) 2007/10/21
- C++ ABI의 안정성: 프로그래밍 언어의 진화 (댓글 1개 / 트랙백 0개) 2006/02/23
- 솔라리스 디바이스 드라이버 작성을 원하십니까? (댓글 1개 / 트랙백 0개) 2008/09/18
- DLight 소개 (댓글 0개 / 트랙백 0개) 2009/06/29
- Dmalloc 을 솔라리스, 썬 스튜디오 컴파일러와 사용하기 (댓글 6개 / 트랙백 0개) 2007/06/13
댓글을 달아 주세요
좋은 정보 감사해요~
2007/09/19 04:16