[WWDC19] LLDB: "po"를 넘어서 (LLDB: Beyond "po")

권승용·2022년 6월 23일
2

WWDC 비디오 정리

목록 보기
2/2
post-thumbnail

WWDC19 비디오 LLDB: Beyond "po"의 12분 15초까지의 내용을 정리하였습니다.


LLDB란?

  • LLDB는 앱이 실행중일 때 디버깅할 수 있는 도구이다.
  • Low-Level Debugger ( C / C++ / Objective-C / Swift 지원)
  • Xcode의 기본 디버거로 내장되어 있다.
  • 런타임 시 일어나는 논리적 오류를 잡는 것이 목표이다.

po 명령어에 대하여

// 예시 코드
struct Trip {
	var name: String
    var destinations: [String]
}

let cruise = Trip (
	name: "Mediterranean Cruise",
    destinations: ["Sorrento", "Capri", "Taormina"])
  • po란 무엇인가?

    po는 lldb 명령어로써, 내가 설정한 변수 객체에 대한 설명을 출력한다. print object의 약자로써 명령어가 만들어졌다.

  • po 명령어로 반환되는 object 설명 customize 하기

    CustomDebugStringConvertible 프로토콜을 준수하도록 작성
    -> debugDescription 프로퍼티 작성 필요

    // 예시 코드
    struct Trip {
    	var name: String
        var destinations: [String]
    }
    // 프로토콜 준수
    extension Trip: CustomDebugStringConvertible {
    	var debugDescription: String { "Trip description"}
    }
    
    let cruise = Trip (
    	name: "Mediterranean Cruise",
        destinations: ["Sorrento", "Capri", "Taormina"])

    적용 결과 top-level description이 바뀐 모습을 확인 가능하다.

  • 기타 po의 사용법

    po는 임의의 표현식을 평가할 수 있다.
    예를 들어, 변수의 대문자 버전을 연산해 보거나 destinations 배열의 내용을 알파벳 순으로 정렬해서 출력해 볼 수도 있다.

  • 사실 po는 expression 명령어의 alias이다.

    따라서 본인만의 po 명령어를 만들어 사용할 수도 있다.

  • po는 어떻게 작동하는가?


    1. LLDB는 표현식 그 자체를 평가하지 않고 주어진 표현식으로부터 컴파일될 수 있는 작은 소스 코드 조각을 생성한다.
    2. 컴파일러에게 요청해서 코드를 컴파일 한 후 실행한다.
    3. 실행이 완료되면 LLDB는 결과값에 접근할 수 있다.
    4. 그 후 object description을 위해 또 다른 코드 조각을 생성한다.
    5. 마찬가지로 컴파일한 후 실행한다.
    6. 결과 문자열을 얻은 후 출력한다.


p 명령어에 대하여

  • p 명령어란?

    p 명령어는 LLDB에서 변수를 출력하는 또 다른 방식이다.
    object description을 빼고 print하는 것으로 생각하면 된다.
    po를 사용했을 때와의 차이점은 내용은 첫째로 같지만 표현 방식이 다르다는 것이고, 둘째로는 결과값이 $R0이라는 이름을 갖게 되었다는 것이다.

  • $R0에 대하여

    $R0, $R1 등은 LLDB에서의 특별한 convention이다. 각 표현식의 결과는 $R1, $R2 등의 증가하는 이름으로 주어진다. LLDB에서는 $R0을 프로젝트 내의 다른 변수처럼 접근할 수 있다.

  • p는 어떻게 작동하는가?

    1. po와 비슷하게, p는 first-class 명령어가 아닌 expression 명령어의 alias이다.
    2. 처음 부분은 po가 작동하는 방식과 같다.
    3. 그러나 Get result 이후 LLDB는 Dynamic type resolution이라는 단계를 실행한다.
    4. 결과값에 대한 dynamic type resolution을 실행한 후 LLDB는 결과 객체를 formatter subsystem에 넘겨 사람이 읽을 수 있는 object description으로 변환하여 출력한다.

    Dynamic type resolution
    Dynamic type resolution 이란, 변수의 static 타입과 dynamic 타입 중 가장 정확한 타입을 찾는 것이다.

    //예시 코드 
    protocol Activity {}
    struct Trip: Activity {
       var name: String
       var destination: [String]
    }
    let cruise: Activity = Trip(...) 
    // 여기서 Activity는 static 타입, Trip은 dynamic 타입이다.
    // 두 타입이 일치하지 않기 때문에, LLDB는 가장 정확한 타입을 찾으려고 한다. 
    // 그것이 바로 Dynamic type resolution 이다. 
  • Formatter는 어떻게 작동하는가?

    추후 작성


v 명령어에 대하여

  • v 명령어란?

    v 명령어의 output은 p 명령어의 출력과 정확히 같다. 그 이유는 v 명령어도 위에서 설명한 formatter를 사용하기 때문이다.

    또한 v 명령어는 위의 두 명령어들처럼 하나의 alias이며, frame variable 명령어의 alias이다.
    다른 명령어들과 달리 v 명령어는 코드 컴파일 과정이 없어서 굉장히 빠르다.
    그 대신 코드 컴파일 과정이 없기 때문에 expression을 평가하는 작업에는 사용할 수 없다.

  • v는 어떻게 작동하는가?

    v는 변수를 출력하기 위해 먼저 program state를 참조하여 메모리에서 변수의 위치를 찾은 후, 변수의 value를 읽어들인다. 그 후 변수에 대해 dynamic type resolution을 실행하고, 만약 유저가 subfield에 대해서도 접근하기를 요청했다면 각 subfield들에 대해서도 단계를 반복한다. 모든 것이 끝나면 data formatter subsystem에 결과를 전달한다.

    여기서 중요한 점은 만약 여러 subfield에 대해 접근해야 한다면 v 명령어는 p나 po와 달리 dynamic type resolution을 여러 번 수행할 가능성이 생긴다는 것이다.


p, po, v의 비교

profile
ios 개발자 지망생 입니다!

0개의 댓글