• Debugging Model

    • 디버깅은 잘못된 동작이 일어났을 때 시작된다.

      • 크래시, 잘못된 값 출력, 행 등

      • 프로그램 시작부터 잘못된 결과가 나온 시점 사이 어딘가에 오류가 있는 코드가 실행되었을 것이다. 디버깅은 이 코드를 찾는 것을 목표로 한다.

        • 디버깅은 검색 문제

        스크린샷 2024-07-06 오전 11.55.59.png

    • 기존에 익숙한 방법

      • 로그 심기

        • 프로그램의 특정 상태를 검사하는 방법

        • 로그가 세밀할수록 버그를 찾는데 필요한 정보가 있을 가능성이 높다.

        • 프로그래머가 사전에 로그 남길 정보를 정해야 하지만, 사용자가 개발자 사이에서 앱 진단을 전달할 좋은 방법이 될 수 있다.

          스크린샷 2024-07-06 오후 12.12.46.png

      • 프린트 디버깅

        • 이 방법들은 시간도 많이 들고 에러 내기도 쉽다.

        스크린샷 2024-07-06 오후 12.14.15.png

    • 디버거를 사용하면 빠르게 문제 영역을 탐색할 수 있다.

      • lldb가 제공하는 4가지 도구: backtraces, Variable inspection, Breakpoints, Expression Evaluation

        스크린샷 2024-07-06 오후 12.16.22.png

    • 디버거 워크플로우

      • 실행, 정지 탐색

      • 더 진행할수도 있고, 이전 상태를 보려면 다시 실행할 수도 있다.

        스크린샷 2024-07-06 오후 12.18.16.png

  • Starting the debug session

    • 시작하려면

      • Xcode에서 Run
      • lldb — <executable> <arguments…>
    • lldb는 크래시 로그를 받아서 디버깅 세션처럼 재현하는 기능을 갖추고 있다.

      • 크래시 로그를 Xcode로 실행하고 해당하는 Xcode Project를 실행하면 된다.
      • 앱을 제출한 상태와 같은 커밋인지 확인할 것
      • 해당 빌드의 dSYM 번들이 있어야 한다.

      스크린샷 2024-07-06 오후 12.20.58.png

    • Backtrace: 일련의 함수 호출이나 스택프레임을 알려줘서 각 함수의 호출 당시 상태와 반환시점을 알려준다.

      • 어떻게 프로그램이 현재의 상태로 빠졌는가를 알려준다.
  • Breaking

    • breakpoint resolving: 한줄에만 브레이크 포인트를 걸어도 실제로는 여러개로 쪼개질 수 있다.
      • 해당 라인이 여러개의 코드 경로를 가짐을 의미한다.

      • 앱이 실행될 떄 LLDB가 리졸빙을 한다.

        스크린샷 2024-07-06 오후 12.31.41.png

      • 이렇게 리졸빙된 브레이크포인트는 Xcode의 네비게이터에서도 볼 수 있고 디버그 커맨드로도 볼 수 있다.

        • 브레이크 포인트들은 개별적인 ID를 부여받는다.

        스크린샷 2024-07-06 오후 12.34.18.png

      • 첫번째는 Button생성자

        스크린샷 2024-07-06 오후 12.34.31.png

      • 두번째는 action클로저

        스크린샷 2024-07-06 오후 12.35.02.png

      • 세번째는 trailing closure

        • 시작은 중괄호지만, 실제로는 다음 줄로 잡힌다.

        스크린샷 2024-07-06 오후 12.35.14.png

        스크린샷 2024-07-06 오후 12.38.50.png

      • 이렇게 멈춘다음에 프로그램의 상태를 확인해야 한다.

        • p 등의 디버그 커맨드 사용
        • 아예 브레이크포인트가 걸릴때마다 특정 커맨드를 실행할 수도 있다.
      • 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> // 특정 키워드와 연관된 커맨드나 옵션을 알려줌
          

          스크린샷 2024-07-06 오후 12.48.15.png

        • 자주 불리는 브레이크포인트 중에서 일부만 필터링하기

          • 조건 설정 기능(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가 되면서 대부분의 경우를 커버한다.

      • 변수를 출력하고 식을 평가할 수 있다.
      • backtrace의 어떤 프레임에서도 동작한다.
      • 재빌드하지 않고도 점점 더 복잡한 식을 평가해가면서 디버깅할 수 있게 됨

      스크린샷 2024-07-06 오후 10.38.11.png

    • Swift Error Breakpoint: Swift 에러를 던지면 프로그램이 잠시 멈춘다.

    • 커스텀 타입 요약하기

      • 프로퍼티가 너무 많거나 콜렉션 안에 있는 경우에는 요약본이 필요하다.
      • Swift6부터는 p 커맨드의 결과물도 커스텀이 가능하다.
        • @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으로 대체 가능하다.

  • 버그를 찾았으면 테스트 커버리지에 추가할 것