programing

python 추적 분할 결함

bestcode 2022. 11. 18. 21:37
반응형

python 추적 분할 결함

Python에서 C 확장을 개발 중인데 몇 가지 세그먼트 폴트(seg faults)를 획득했습니다.

segfault가 발생하는 코드 행을 표시하는 방법을 찾고 있습니다(코드 한 줄 한 줄씩 추적하는 것과 같습니다). 어떻게 하면 될까요?

Linux의 경우 gdb에서 python을 실행합니다.

gdb python
(gdb) run /path/to/script.py
## wait for segfault ##
(gdb) backtrace
## stack trace of the c code

다음은 코드가 실행되는 Python의 모든 줄의 파일 이름과 줄 번호를 출력하는 방법입니다.

import sys

def trace(frame, event, arg):
    print("%s, %s:%d" % (event, frame.f_code.co_filename, frame.f_lineno))
    return trace

def test():
    print("Line 8")
    print("Line 9")

sys.settrace(trace)
test()

출력:

call, test.py:7
line, test.py:8
Line 8
line, test.py:9
Line 9
return, test.py:9

(물론 트레이스 출력을 파일에 쓰는 것이 좋습니다).

저는 같은 문제에 대한 해결책을 찾아 이곳에 왔지만, 다른 답변들은 전혀 도움이 되지 않았습니다.도움이 된 것은 이며, Python 2.7에 설치할 수 있습니다.pip install.

faulthandlerPython은 2012년 9월에 출시된 버전 3.3에서만 소개되었으며, 이 버전에서는 대부분의 다른 답변이 작성된 후였습니다.

gdb에는 문서화되어 있지 않은 python 확장자가 있습니다.

Python 소스 grab(일반 설치에는 포함되지 않음)에서 가져옵니다.

이거 넣어주세요sys.path

그 후, 다음과 같이 입력합니다.

# gdb /gps/python2.7_x64/bin/python coredump
...
Core was generated by `/usr/bin/python script.py'.
Program terminated with signal 11, Segmentation fault.
#0  call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037
...
(gdb) python
>import libpython
>
>end
(gdb) bt
#0  call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037
#1  PyEval_EvalFrameEx (f=f@entry=
    Frame 0x7f9084d20ad0, 
    for file /usr/lib/python2.7/site-packages/librabbitmq/__init__.py, line 220, 
    in drain_events (self=<Connection(channels={1: <Channel(channel_id=1, connection=<...>, is_open=True, connect_timeout=4, _default_channel=<....(truncated), throwflag=throwflag@entry=0) at Python/ceval.c:2681
...
(gdb) py-list
 218            else:
 219                timeout = float(timeout)
>220            self._basic_recv(timeout)
 221
 222        def channel(self, channel_id=None):

보시다시피 CPython 콜 체인에 대응하는 Python 스택을 확인할 수 있습니다.

몇 가지 주의사항:

  • 사용하시는 gdb 버전은 7보다 커야 하며 컴파일은 다음과 같이 해야 합니다.--with-python
  • gdbpython을 내장합니다(링크에 의해).libpython)는 서브셸로 실행되지 않습니다.즉, 현재 설치되어 있는 python 버전과 반드시 일치하지는 않을 수 있습니다.$PATH.
  • 다운로드가 필요합니다.libpython.py모든 버전의 Python 소스와 일치합니다.gdb에 링크되어 있습니다.
  • gdb를 root으로 실행해야 할 수 있습니다.그럴 경우 셋업이 필요할 수 있습니다.sys.path디버깅하고 있는 코드의 코드에 일치합니다.

복사할 수 없는 경우libpython.py안으로sys.path위치를 추가할 수 있습니다.sys.path다음과 같습니다.

(gdb) python
>import sys
>sys.path.append('/path/to/containing/dir/')
>import libpython
>
>end

이것은 python dev docs, fedora wiki 및 python wiki에 다소 문서화되어 있지 않습니다.

나이가 많으시면gdb또는 이 기능을 사용할 수 없는 경우 Python 소스의 gdbinit도 복사 가능합니다.~/.gdbinit비슷한 기능이 추가되어 있습니다.

C 확장의 세그먼트 장애는 개체에 대한 새 참조를 생성할 때 참조 카운트가 증가하지 않아 발생하는 경우가 매우 많습니다.따라서 segfault는 오브젝트에서 마지막 참조가 삭제된 후에만 발생하며 다른 오브젝트가 할당되어 있을 때만 발생하는 경우가 많기 때문에 추적하기가 매우 어렵습니다.

지금까지 C 확장 코드를 얼마나 썼는지는 말하지 않았지만, 이제 막 시작한 경우 ctype과 Cython 중 하나를 사용할 수 있는지 고려해 보십시오.Ctype은 요구에 따라 유연하지 않을 수 있지만 Cython을 사용하는 거의 모든 C 라이브러리에 링크하여 모든 참조 카운트를 자동으로 유지할 수 있어야 합니다.

Python 객체와 기본 C 객체의 수명이 다르더라도 문제가 발생할 수 있지만 상당히 단순해집니다.

다음 3가지 대안을 제시하겠습니다.

1: faulthandler를 이노블로 한 스크립트 실행:

python3 -X faulthandler your_script.py

2: 디버깅모드에서의 스크립트 실행(pdb)

python3 -m pdb your_script.py

continue 명령어를 사용하여 스크립트를 실행합니다.

gdb툴은 가장 많은 정보를 제공하지만 스크립트에서 마지막으로 실행된 행 번호를 인쇄하지 않습니다.

3: pytest를 사용하게 되었습니다.이 기능이 작동하기 위해 코드를 다음 함수로 묶었습니다.test_스크립트를 다음과 같이 실행합니다.

pytest your_script.py

언급URL : https://stackoverflow.com/questions/2663841/python-tracing-a-segmentation-fault

반응형