본문 바로가기

이글루스

지훈현서

검색페이지 이동

사이드 메뉴

이글루스 블로그 정보

[Python] Segmentation Fault 발생 시 gdb로 stack trace 해 보기

앱으로 보기

본문 폰트 사이즈 조절

이글루스 블로그 컨텐츠

파이썬으로 개발을 하다보면 제일 까다로운 문제 중의 하나는 
갑자기 Python (CPython) 프로그램 자체가 Segmentation Fault 를 발생하고 
죽는 문제 입니다.

일반적인 파이썬 프로그램 자체는 C로 작성한 프로그램 처럼 
Segmentation Fault 를 발생하며 죽는 경우는 거의 없습니다.

대신, 외부 so 모듈을 이용한다든지 등에서 충분히 발생할 수 있습니다.

우선 테스트한 머신은 우분투 16.04 LTS 버전 입니다.


다음과 같은 파이썬 스크립트가 있습니다.

$ cat dumpcore.py 
class Foo:

    def bar(self):
        from ctypes import string_at
        string_at(0xDEADBEEF) # this code will cause Python to segfault


def main():
    f = Foo()
    f.someattr = 42
    f.someotherattr = {'one':1, 'two':2, 'three':[(), (None,), (None, None)]}
    f.bar()


if __name__ == "__main__":
    main()

위의 프로그램을 파이썬 3.5에서 돌려보면 (VirtualEnv 이용)

(python3) future@FS:~$ python dumpcore.py 
Segmentation fault (core dumped)

마찬가지로 파이썬 2.7에서도
(python2) future@FS:~$ python dumpcore.py 
Segmentation fault (core dumped)

동일하게 오류가 발생합니다.

발생 원인은 직접 ctypes로 접근 불가능한 메모리를 접근하려고 하기 때문에
발생하는 것입니다.
(C 에서도 아주 쉽게 포인터 참조 오류가 발생할 수 있지요)

이와 같은 경우 gdb로 쉽게 원인을 찾을 수 있는 방법을 소개 합니다.


1) libpython.py 다운로드

$ mkdir -p ~/.config/gdb && cd ~/.config/gdb
# python 3.5 인 경우
$ wget https://hg.python.org/cpython/rawfile/3.5/Tools/gdb/libpython.py
# python 2.7 인 경우
$ wget https://hg.python.org/cpython/rawfile/2.7/Tools/gdb/libpython.py


2) .gdbinit

$ vi ~/.gdbinit
python
import gdb
import sys
import os
sys.path.insert(0, os.path.expanduser("~/.config/gdb"))
def setup_python(event):
    import libpython
gdb.events.new_objfile.connect(setup_python)
end


3) gdb 실행

참고! python 2.7 인 경우에는 python-dbg 를 이용해야 합니다.
# python 2.7 인 경우
$ sudo apt-get install python-dbg

# python 2.7 인 경우
$ gdb --args python-dbg dumpcore.py

# python 3.5 인 경우
$ gdb --args python dumpcore.py

...
(gdb) run
해당 스크립트를 실행하면...

Starting program: /opt/python3/bin/python dumpcore.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb)

위와 같이 세그폴트가 발생하였음을 알 수 있구요...

만약 일반적인 stack trace 인 

(gdb) bt
#0  strlen () at ../sysdeps/x86_64/strlen.S:106
#1  0x00007ffff6764cd9 in string_at.lto_priv.109 (ptr=0xdeadbeef <error: Cannot access memory at address 0xdeadbeef>, 
    size=<optimized out>) at /build/python3.5-ZBQ2p1/python3.5-3.5.2/Modules/_ctypes/_ctypes.c:5219
#2  0x00007ffff6772e20 in ffi_call_unix64 () from /opt/python3/lib/python3.5/lib-dynload/_ctypes.cpython-35m-x86_64-linux-gnu.so
#3  0x00007ffff677288b in ffi_call () from /opt/python3/lib/python3.5/lib-dynload/_ctypes.cpython-35m-x86_64-linux-gnu.so
#4  0x00007ffff676d01a in _call_function_pointer (argcount=2, resmem=0x7fffffffd970, restype=<optimized out>, 
    atypes=<optimized out>, avalues=0x7fffffffd950, pProc=0x7ffff6764cc0 <string_at.lto_priv.109>, flags=4357)
    at /build/python3.5-ZBQ2p1/python3.5-3.5.2/Modules/_ctypes/callproc.c:811
#5  _ctypes_callproc (pProc=0x7ffff6764cc0 <string_at.lto_priv.109>, argtuple=<optimized out>, flags=4357, 
    argtypes=(<built-in method from_param of _ctypes.PyCSimpleType object at remote 0xb3ecd8>, <built-in method from_param of _ctypes.PyCSimpleType object at remote 0xb39768>), restype=<_ctypes.PyCSimpleType at remote 0xb35ac8>, checker=0x0)
    at /build/python3.5-ZBQ2p1/python3.5-3.5.2/Modules/_ctypes/callproc.c:1149
#6  0x00007ffff6760fcb in PyCFuncPtr_call.lto_priv.89 (self=self@entry=0x7ffff7fc9d90, inargs=<optimized out>, 
    kwds=<optimized out>) at /build/python3.5-ZBQ2p1/python3.5-3.5.2/Modules/_ctypes/_ctypes.c:3856
#7  0x00000000005bc727 in PyObject_Call () at ../Objects/abstract.c:2165
#8  0x000000000051f756 in do_call (nk=<optimized out>, na=<optimized out>, pp_stack=0x7fffffffdc70, func=<optimized out>)
    at ../Python/ceval.c:4936
...

위와 같이 C레벨의 코드가 나와 어디서 부터 오류가 발생하였는지 알 수 없습니다.

대신, py-bt 라는 명령을 내리면,

(gdb) py-bt
Traceback (most recent call first):
  File "/usr/lib/python3.5/ctypes/__init__.py", line 491, in string_at
    return _string_at(ptr, size)
  File "dumpcore.py", line 5, in bar
    string_at(0xDEADBEEF) # this code will cause Python to segfault
  File "dumpcore.py", line 12, in main
    f.bar()
  File "dumpcore.py", line 16, in <module>
    main()

위와 같이 깔끔하게 dumpcore.py 스크립트의 5번째 줄에서 오류가 발생되었음을 확인할 수 있습니다.


참고로

(gdb) py-list
 486    _string_at = PYFUNCTYPE(py_object, c_void_p, c_int)(_string_at_addr)
 487    def string_at(ptr, size=-1):
 488        """string_at(addr[, size]) -> string
 489    
 490        Return the string at addr."""
>491        return _string_at(ptr, size)
 492    
 493    try:
 494        from _ctypes import _wstring_at_addr
 495    except ImportError:
 496        pass

위와 같이 py-list 명령으로 마지막 오류 발생 파이썬 스크립트를 직접 확인할 수 있습니다.


어느분께는 도움이 되셨기를...


포스트 공유하기

썸네일
지훈현서아빠님의 글 구독하기
덧글 5 관련글(트랙백) 0
신고
맨 위로
앱으로 보기 배너 닫기

공유하기

주소복사

아래의 URL을 길게 누르면 복사할수있습니다.

http://mcchae.egloos.com/m/11230168
닫기

팝업

모바일기기에서만 이용이 가능합니다.
운영체제가 안드로이드, ios인
모바일 기기에서 이용해주세요.

덧글 삭제

정말 삭제하시겠습니까?

비밀번호 확인

게시글 신고하기

밸리 운영정책에 맞지 않는 글은 고객센터로
보내주세요.

신고사유


신고사유와 맞지 않을 경우 처리되지 않을 수 있습니다.
저작권 위반/명예훼손 등은 고객센터를 통해 권리침해
신고해주세요.
고객센터 바로가기