Debugging Model
디버깅은 잘못된 동작이 일어났을 때 시작된다.
크래시, 잘못된 값 출력, 행 등
프로그램 시작부터 잘못된 결과가 나온 시점 사이 어딘가에 오류가 있는 코드가 실행되었을 것이다. 디버깅은 이 코드를 찾는 것을 목표로 한다.
기존에 익숙한 방법
로그 심기
프로그램의 특정 상태를 검사하는 방법
로그가 세밀할수록 버그를 찾는데 필요한 정보가 있을 가능성이 높다.
프로그래머가 사전에 로그 남길 정보를 정해야 하지만, 사용자가 개발자 사이에서 앱 진단을 전달할 좋은 방법이 될 수 있다.
프린트 디버깅
디버거를 사용하면 빠르게 문제 영역을 탐색할 수 있다.
lldb가 제공하는 4가지 도구: backtraces, Variable inspection, Breakpoints, Expression Evaluation
디버거 워크플로우
실행, 정지 탐색
더 진행할수도 있고, 이전 상태를 보려면 다시 실행할 수도 있다.
Starting the debug session
시작하려면
lldb는 크래시 로그를 받아서 디버깅 세션처럼 재현하는 기능을 갖추고 있다.
Backtrace: 일련의 함수 호출이나 스택프레임을 알려줘서 각 함수의 호출 당시 상태와 반환시점을 알려준다.
Breaking
해당 라인이 여러개의 코드 경로를 가짐을 의미한다.
앱이 실행될 떄 LLDB가 리졸빙을 한다.
이렇게 리졸빙된 브레이크포인트는 Xcode의 네비게이터에서도 볼 수 있고 디버그 커맨드로도 볼 수 있다.
첫번째는 Button생성자
두번째는 action클로저
세번째는 trailing closure
이렇게 멈춘다음에 프로그램의 상태를 확인해야 한다.
CLI로 다루기
일단 앱을 정지한 상태여야 lldb cli 사용 가능
b(breakpoint set): 브레이크포인트를 설정한다.
(lldb) b DetailView.swift:70
Breakpoint 2: 3 locations.
가장 최근에 설정한 브레이크포인트에 커맨드 추가
(lldb) break command add
Enter your debuffer command(s). Type 'DONE' to end.
>
특정 커맨드 사용법 알기
(lldb) help <command>
(lldb) help <command> <option>
(lldb) apropros <keyword> // 특정 키워드와 연관된 커맨드나 옵션을 알려줌
자주 불리는 브레이크포인트 중에서 일부만 필터링하기
조건 설정 기능(GUI로도 가능)
(lldb) break modify <breakpoint ID> --condition "video.length > 60"
심화 사용예시: 해당 브레이크포인트에 걸렸을 때 임시 브레이크포인트를 만들고(tbreak) 자기 자신은 자동으로 continue
정해진 수만큼 무시하기
break modify <breakpoint ID> --ignore-count 10
너무 많이 호출되는 라인의 경우는 오히려 디버거가 매번 조건 판단등을 하느라 실행이 느려질 수 있다.
이때는 차라리 재컴파일이 더 나을 수 있다.
이때는 SIGSTOP을 던지면 프로그램이 Xcode나 lldb를 통해서 돌아가고 있다면 브레이크포인트처럼 동작한다.
for video in videos {
if (/* breakpoint condition */) {
raise(SIGSTOP)
}
if (video.hasRemoteMedia) {
video.loadRemoteMedia()
}
processVideo(video)
}
Inspecting program state
기존에는 상태를 살펴보기 위한 다양한 커맨드가 있었고 다 나름의 사용처가 있었다.
(lldb) p
(lldb) po
(lldb) register read
(lldb) memory read
(lldb) target variable
Xcode 15부터는 p가 dwim-print의 alias가 되면서 대부분의 경우를 커버한다.
Swift Error Breakpoint: Swift 에러를 던지면 프로그램이 잠시 멈춘다.
커스텀 타입 요약하기
@DebugDesription 어노테이션 추가
debugDesription 프로퍼티를 제공하면 되는데, string interpolation과 stored property를 사용할 수 있다.
@DebugDescription
struct WatchLaterItem {
let video: Video
let name: String
let addedOn: Date
var debugDescription: String {
"\\(name) - \\(addedOn)"
}
}
po를 커스텀하는데 쓰던 CustomDebugStringConvertible을 쓰고 있었다면 @DebugDesription으로 대체 가능하다.
버그를 찾았으면 테스트 커버리지에 추가할 것