- 작성시간 : 2016/08/24 12:02
- 퍼머링크 : mcchae.egloos.com/11230168
- 덧글수 : 5
파이썬으로 개발을 하다보면 제일 까다로운 문제 중의 하나는

갑자기 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 명령으로 마지막 오류 발생 파이썬 스크립트를 직접 확인할 수 있습니다.
어느분께는 도움이 되셨기를...
덧글
위와 같은 문제가 주로 발생합니다.
암튼 도움이 되셨다니 저의 보람입니다~ ^^