본문 바로가기

[Debugging] Tips

[TRACE32] gcore 크래시 유틸리티로 유저 프로세스의 콜스택 보기

이번 포스팅에서는 크래시 유틸리티의 extensions인 gcore를 빌드하는 방법과 이 기능을 활용해 유저 프로세스의 스택을 추출하는 방법을 소개한다.
 
* gcore 소스 코드를 내려받기
 
'http://people.redhat.com/~anderson/extensions.html' url에 액세스한 다음에, 
crash-gcore-command-1.5.1.tar.gz 파일을 내려받는다.
 
crash-gcore-command-1.5.1.tar.gz
 
crash-gcore-command-1.5.1.tar.gz 파일을 받은 다음에 압축을 푼다.
 
* gcore 소스 코드를 빌드하기
 
먼저 크래시 유틸리티의 소스 코드를 빌드한다. (ARM64 아키텍처를 기준)
$ make target=ARM64
 
크래시 유틸리티의 소스에서 extensions 디렉토리로 이동한다.
 
austindh.kim:~/bin/crash_src/crash/extensions$ ls
COPYING  defs.h  dminfo.c  echo.c  eppic  eppic.c  eppic.mk  libgcore  Makefile  snap.c  snap.mk  trace.c
 
gcore 유틸리티의 소스 코드를 extensions 폴더로 복사한다.
 
austindh.kim:~/bin/crash_src/crash/extensions$ cp -r ~austindh.kim/bin/gcore-command-1.5.1/crash-gcore-command-1.5.1/* .
austindh.kim:~/bin/crash_src/crash/extensions$ ls
COPYING  defs.h  dminfo.c  echo.c  eppic  eppic.c  eppic.mk  gcore.c  gcore.mk  libgcore  Makefile  snap.c  snap.mk  trace.c
 
gcore의 소스 코드를 복사한 후 gcore.c와 gcore.mk 파일이 보일 것이다. 그럼 제대로 복사가 된 것이다.
 
크래시 유틸리티의 최상단 디렉토리로 이동한 후 'make target=ARM64 extensions' 명령어를 입력해 extensions 기능을 빌드한다. 
 
austindh.kim:~/bin/crash_src/crash$ make target=ARM64 extensions
gcc -Wall -g -shared -rdynamic -o echo.so echo.c -fPIC -DARM64  -DGDB_7_6
cd eppic/libeppic && make
 
extensions 디렉터리로 이동하면 gcore.so이란 라이브러리 파일이 제대로 생성됐음을 확인할 수 있다.
 
austindh.kim:~/bin/crash_src/crash/extensions$ ls
COPYING  dminfo.c   echo.c   eppic    eppic.mk  gcore.c   gcore.so  Makefile  snap.mk  trace.so
defs.h   dminfo.so  echo.so  eppic.c  eppic.so  gcore.mk  libgcore  snap.c    trace.c
 
크래시 유틸리티를 실행한 다음에 gcore.so 라이브러리 파일을 extension 명령어로 로딩한다.
 
crash64> extend /home007/austindh.kim/bin/Crash64Tool/extensions/gcore.so
/home007/austindh.kim/bin/Crash64Tool/extensions/gcore.so: shared object loaded
 
'shared object loaded' 메시지가 보이니 제대로 gcore.so 라이브러리 파일이 로딩됐음을 알 수 있다.
 
* 크래시 유틸리티에서 gcore.so 파일을 로딩하기
 
이제 PID가 1인 init 프로세스의 유저 공간을 덤프해보자.
이를 위해 'gcore [pid]' 명령어를 입력해야 한다.
 
crash64> gcore 1
gcore: WARNING: page fault at 64a000
gcore: WARNING: page fault at 64b000
gcore: WARNING: page fault at 64e000
gcore: WARNING: page fault at 651000
gcore: WARNING: page fault at 652000
gcore: WARNING: page fault at 659000
gcore: WARNING: page fault at 7f78601000
gcore: WARNING: page fault at 7f78602000
...
gcore: WARNING: page fault at 7fc2740000
gcore: WARNING: page fault at 7fc2741000
Saved core.1.init
 
'WARNING: page fault' 메시지는 False Positive 이니 너무 겁먹지 말자.
 
* TRACE32로 gcore 덤프 파일을 로딩하기
 
이제 gcore로 추출한 덤프 파일을 TRACE32 프로그램으로 로딩하자.
 
Data.LOAD.binary core.1.init 0x7FC20FD000
Data.LOAD.elf init 
 
참고로 0x7FC20FD000
 오프셋은 gcore 메타 해더 오프셋을 고려해 계산했다.
0x7FC20FD000 = 0x7FC2742000 - 0x645000
 
커널 스택 주소의 스택 최하단에 위치한 '유저 프로세스의 레지스터 세트'정보를 참고해 레지스터 세트를 설정한다.
TRACE32 명령어는 다음과 같다.
 
$ r.s X29   0x7FC2744110
$ r.s X30                  0
$ r.s PC            0x419344
$ r.s SP    0x7FC27440D0
$ v.f
 
이제 유저 공간에서 실행 중인 init 프로세스의 콜스택을 볼 수 있다.
 
-000|std::__1::__compressed_pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allo
-000|std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::basic_string(
-000|android::init::SocketConnection::source_context(?)
-001|std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::basic_string(
-001|android::init::handle_property_set_fd()
-002|android::init::main(?, ?)
-003|main(?, ?)
-004|__libc_init(?, ?, slingshot = 0x00403698, structors = 0x0000007FC2744600)
 ---|end of frame