본문으로 건너뛰기
김신건의 로그

[Python] pdb, breakpoint(), 디버깅 도구

· 수정 · 📖 약 2분 · 817자/단어 #python #pdb #debugger #stdlib #debugging
python pdb, breakpoint, ipdb, pudb, debugpy, PYTHONBREAKPOINT

정의

pdb는 Python 표준 라이브러리의 대화형 디버거. 중단점, 스텝 실행, 변수 검사, 콜 스택 탐색을 제공. 3.7+ breakpoint() 빌트인이 표준 진입점.

breakpoint()

def process(items):
    for i, item in enumerate(items):
        breakpoint()        # 여기서 디버거 진입
        result = transform(item)

breakpoint() 호출 지점에서 pdb 시작. 다른 디버거(ipdb, pudb, debugpy)를 쓰려면 환경변수.

PYTHONBREAKPOINT=ipdb.set_trace python script.py
PYTHONBREAKPOINT=0 python script.py    # 모든 breakpoint 비활성화 (프로덕션)

PYTHONBREAKPOINT=0은 코드를 안 고치고 디버깅 비활성화. 배포 환경 유용.

pdb 핵심 명령어

명령동작
h도움말
l현재 줄 주변 코드 표시
ll함수 전체
n다음 줄 (step over)
sstep into (함수 안으로)
rstep out (함수 끝까지)
ccontinue (다음 중단점까지)
j Njump to line N
p expr표현식 출력
pp exprpretty print
wwhere (콜 스택)
uup (호출자로)
ddown (호출 받은 곳으로)
b 42line 42에 중단점
b foo.py:42특정 파일/줄
b func_name함수 진입 시
b 42, x > 0조건부 중단점
cl모든 중단점 제거
cl 11번 중단점 제거
q종료
interact인터프리터 모드 진입

예제 세션

def divide(a, b):
    breakpoint()
    return a / b

divide(10, 0)
> script.py(3)divide()
-> return a / b
(Pdb) l
  1     def divide(a, b):
  2         breakpoint()
  3  ->     return a / b
  4
  5     divide(10, 0)

(Pdb) p a, b
(10, 0)
(Pdb) p a / b
*** ZeroDivisionError: division by zero

(Pdb) w
  script.py(5)<module>()
> script.py(3)divide()

(Pdb) q

사후 디버깅 (post-mortem)

예외 후 디버거 진입.

import pdb

try:
    risky()
except:
    pdb.post_mortem()    # 예외 발생 지점에서 시작

또는 CLI:

python -m pdb script.py        # 시작부터
python -m pdb -c continue script.py    # 첫 예외에서 정지

조건부 중단점

(Pdb) b 42, x > 100 and y is None

루프 안에서 특정 조건 만족 시만 정지. 1000회 반복 중 1번을 찾을 때 유용.

표현식 모니터링: display

(Pdb) display x
(Pdb) display
(Pdb) n
display x: 5  [old: 4]

매 step마다 값 변화 자동 표시.

ipdb: IPython 기반

pip install ipdb
import ipdb; ipdb.set_trace()
# 또는
PYTHONBREAKPOINT=ipdb.set_trace python script.py

자동 완성, 색상, 더 풍부한 출력. 개발 환경에 추천.

pudb: 풀스크린 TUI

pip install pudb
import pudb; pu.db    # 트레이스 시작
PYTHONBREAKPOINT=pudb.set_trace python script.py

vim 스타일 단축키, 콜 스택·변수·코드 동시 표시. 본격 디버깅에 강력.

debugpy: VS Code, PyCharm

IDE 디버거의 백엔드.

pip install debugpy
import debugpy
debugpy.listen(5678)
debugpy.wait_for_client()    # 디버거 연결 대기
debugpy.breakpoint()

VS Code에서 attach configuration으로 연결. 원격 디버깅, 컨테이너 안 등에 유용.

faulthandler: 크래시 분석

세그폴트, 무한 루프 등 인터프리터 종료 시 트레이스 출력.

import faulthandler
faulthandler.enable()        # SIGSEGV, SIGFPE 등 발생 시 트레이스

또는 시작 시:

python -X faulthandler script.py
PYTHONFAULTHANDLER=1 python script.py

크래시 위치를 파악하는 유일한 방법인 경우 있음.

traceback

import traceback

try:
    work()
except Exception:
    traceback.print_exc()                     # stderr로
    traceback.print_exception(*sys.exc_info())

    s = traceback.format_exc()                # 문자열로

    # 콜 스택 (예외 없이도)
    traceback.print_stack()

로깅과 결합하면 무엇이 어디서 깨졌는지 파일에 저장 가능.

sys.settrace: 커스텀 트레이서

import sys

def tracer(frame, event, arg):
    print(event, frame.f_code.co_name, frame.f_lineno)
    return tracer

sys.settrace(tracer)
my_function()
sys.settrace(None)

이벤트: call, line, return, exception. 디버거·프로파일러 작성의 기반.

자주 보는 디버깅 패턴

빠른 진입

# 코드 안 어디서나
import pdb; pdb.set_trace()

# 또는 (3.7+)
breakpoint()

쓴 다음 즉시 잊지 말 것. 프로덕션 푸시 전 grep으로 확인.

  1. print(x) - 가장 빠름, 좋은 단서
  2. logger.debug("x=%s", x) - 로그 시스템 활용
  3. breakpoint() - 정말 복잡할 때
  4. IDE 디버거 - 인스펙터, 조건부 중단점

print는 절대 부정적인 게 아님. 빠른 가설 검증에 효율적.

의심 함수 추적

def trace(fn):
    import functools
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        print(f"-> {fn.__name__}({args}, {kwargs})")
        result = fn(*args, **kwargs)
        print(f"<- {fn.__name__} = {result!r}")
        return result
    return wrapper

@trace
def suspect(x): ...

또는 icecream 라이브러리:

pip install icecream
from icecream import ic
ic(x, y)        # 변수명 + 값 자동 출력

무한 루프 / 행 (hang)

py-spy dump --pid 12345    # 현재 모든 스레드 스택

또는 SIGUSR1 시그널로 트레이스 덤프 (faulthandler.register).

import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
# kill -USR1 <pid> 으로 트레이스 출력

함정

1. breakpoint() 푸시

def process():
    breakpoint()    # 프로덕션에 푸시되면 서버 정지

→ pre-commit hook으로 차단.

# .pre-commit-config.yaml
- id: no-breakpoint
  name: Check no breakpoint()
  entry: '\bbreakpoint\(\)'
  language: pygrep

2. 디버거 안에서 무한 루프

pdb 안에서 c(continue)했는데 다시 같은 중단점에 걸리는 무한 → cl (모든 중단점 제거).

3. async 함수 디버깅

async def main():
    breakpoint()    # OK, 동작함
    await long()

이벤트 루프가 정지하지만 그 안에서 양보 동작은 불가능. asyncio 전용 디버그 모드:

python -X dev script.py    # asyncio dev mode 등

4. multiprocessing 디버깅

자식 프로세스는 stdin이 닫혀 pdb 입력 불가. 대안: pdb.set_trace(sys.stdin) 우회, 또는 디버그 시 process pool 비활성화.

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기