macOS에서 디버깅하기
JavaScript 애플리케이션이 아닌 Electron 자체에서 발생한 것으로 보이는 크래시나 문제를 겪는다면, 네이티브/C++ 디버깅에 익숙하지 않은 개발자에게는 디버깅이 다소 까다로울 수 있다. 하지만 lldb
와 Electron 소스 코드를 사용하면 Electron 소스 코드 내부에 중단점을 설정하고 단계별 디버깅을 진행할 수 있다. 그래픽 인터페이스를 선호한다면 XCode를 사용한 디버깅도 가능하다.
요구 사항
-
Electron 테스트 빌드: 가장 쉬운 방법은 소스 코드를 직접 빌드하는 것이다. 빌드 가이드에 따라 진행할 수 있다. 다운로드한 Electron에 직접 디버거를 연결해 디버깅할 수 있지만, 이 경우 최적화가 많이 되어 있어 디버깅이 상당히 어려워진다. 이때 디버거는 모든 변수의 내용을 보여주지 못할 수 있으며, 인라이닝(inlining), 테일 콜(tail calls), 그리고 기타 컴파일러 최적화로 인해 실행 경로가 이상하게 보일 수 있다.
-
Xcode: Xcode 외에도 Xcode 커맨드라인 도구를 설치해야 한다. 이 도구에는 macOS에서 Xcode의 기본 디버거인 LLDB가 포함되어 있다. LLDB는 데스크톱과 iOS 기기 및 시뮬레이터에서 C, Objective-C, C++ 코드를 디버깅할 수 있다.
-
.lldbinit: Chromium 코드가 소스 맵핑되도록
~/.lldbinit
파일을 생성하거나 수정한다.# 예: ['~/electron/src/tools/lldb']
script sys.path[:0] = ['<...path/to/electron/src/tools/lldb>']
script import lldbinit
Electron에 연결하고 디버깅하기
디버깅 세션을 시작하려면 터미널을 열고 lldb
를 실행한 후, 릴리스 버전이 아닌 Electron 빌드를 인자로 전달한다.
$ lldb ./out/Testing/Electron.app
(lldb) target create "./out/Testing/Electron.app"
Current executable set to './out/Testing/Electron.app' (x86_64).
중단점 설정하기
LLDB는 강력한 도구로, 코드 검사를 위한 다양한 전략을 지원한다. 이 기본 가이드에서는 JavaScript에서 호출한 명령어가 제대로 동작하지 않을 때, 해당 명령어의 C++ 구현 부분인 Electron 소스 코드 내부에서 중단점을 설정하는 방법을 알아본다.
관련 코드 파일은 ./shell/
디렉토리에서 찾을 수 있다.
app.setName()
을 디버깅하고 싶다고 가정해 보자. 이 함수는 browser.cc
파일에서 Browser::SetName()
으로 정의되어 있다. breakpoint
명령어를 사용해 파일과 줄 번호를 지정하여 중단점을 설정한다:
(lldb) breakpoint set --file browser.cc --line 117
Breakpoint 1: where = Electron Framework`atom::Browser::SetName(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) + 20 at browser.cc:118, address = 0x000000000015fdb4
그런 다음, Electron을 실행한다:
(lldb) run
앱이 즉시 중단된다. Electron은 실행 시 앱의 이름을 설정하기 때문이다:
(lldb) run
Process 25244 launched: '/Users/fr/Code/electron/out/Testing/Electron.app/Contents/MacOS/Electron' (x86_64)
Process 25244 stopped
* thread #1: tid = 0x839a4c, 0x0000000100162db4 Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 20 at browser.cc:118, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100162db4 Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 20 at browser.cc:118
115 }
116
117 void Browser::SetName(const std::string& name) {
-> 118 name_override_ = name;
119 }
120
121 int Browser::GetBadgeCount() {
(lldb)
현재 프레임의 인자와 지역 변수를 확인하려면 frame variable
(또는 fr v
) 명령어를 실행한다. 이 명령어는 앱이 현재 "Electron"이라는 이름을 설정하고 있음을 보여준다.
(lldb) frame variable
(atom::Browser *) this = 0x0000000108b14f20
(const string &) name = "Electron": {
[...]
}
현재 선택된 스레드에서 소스 코드 레벨로 한 단계씩 진행하려면 step
(또는 s
) 명령어를 실행한다. 이 명령어는 name_override_.empty()
로 이동하게 한다. 다음 단계로 넘어가려면 next
(또는 n
) 명령어를 실행한다.
(lldb) step
Process 25244 stopped
* thread #1: tid = 0x839a4c, 0x0000000100162dcc Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 44 at browser.cc:119, queue = 'com.apple.main-thread', stop reason = step in
frame #0: 0x0000000100162dcc Electron Framework`atom::Browser::SetName(this=0x0000000108b14f20, name="Electron") + 44 at browser.cc:119
116
117 void Browser::SetName(const std::string& name) {
118 name_override_ = name;
-> 119 }
120
121 int Browser::GetBadgeCount() {
122 return badge_count_;
참고: 예상대로 소스 코드가 보이지 않는다면, 앞서 설명한 ~/.lldbinit
파일을 추가하지 않았을 수 있다.
이 시점에서 디버깅을 종료하려면 process continue
명령어를 실행한다. 또한 특정 줄에 도달할 때까지 스레드를 실행할 수도 있다 (thread until 100
). 이 명령어는 현재 프레임에서 스레드를 실행하다가 해당 프레임의 100번째 줄에 도달하거나 프레임을 벗어나면 중단한다.
이제 Electron의 개발자 도구를 열고 setName
을 호출하면 다시 중단점에 걸리게 된다.
추가 학습 자료
LLDB는 강력한 도구로, 훌륭한 문서를 제공한다. 더 깊이 이해하기 위해 Apple의 디버깅 문서를 참고할 수 있다. 예를 들어 LLDB 명령어 구조 참조나 독립형 디버거로 LLDB 사용하기 소개 자료를 살펴보는 것이 좋다.
또한, LLDB의 훌륭한 매뉴얼과 튜토리얼을 확인할 수 있다. 이 자료는 더 복잡한 디버깅 시나리오를 설명한다.