DWARF & dSYM

Là một iOS Engineer, mình có chút thắc mắc là làm sao để lldb có thể dừng đúng chỗ mà chúng ta đặt breakpoint. Hay khi đã dừng tại breakpoint thì làm sao nó biết được ở đó có những biến nào để chúng ta debug.

Mình đã tìm hiểu 1 số tài liệu thì đúc kết được một số điều nho nhỏ sau về cách nó hoạt động. Đây chỉ là những gì mình trải nghiệm nên chắc chắn nó không được đầy đủ.

1. DWARF là gì

DWARF là một định dạng dữ liệu được sử dụng để phục vụ mục đích debug chương trình. Nó được thiết kế cùng với Executable & Linkable Format (ELF).

2. DWARF chứa gì

DWARF chứa các Debugging Information Entry(DIE). Có rất nhiều loại DIE được biểu diễn bằng các tag khác nhau. dùng để biểu diễn các thành phần ở trong chương trình, ví dụ DW_TAG_formal_parameter để biểu diễn cho một tham số truyền vào của 1 hàm. Hay DW_TAG_variable để biểu diễn biến. Các TAG này được biểu diễn dưới dạng cây để biểu diễn các thành phần của chương trình.

Các loại DWARF TAG

Chúng ta thử compile chương trình viết bằng swift sau.

Đặt tên source file là main.swift. Chúng ta dùng lệnh swiftc -c -g main.swift; swiftc -o main main.o để biên dịch file swift trên thành object file và link thành excutable file.

Sau khi compile, ta thu được 1 file object main.o và 1 executable main. Thì file main.o bây giờ đã chứa dữ liệu để debugging DWARF bên trong nó.

Chúng ta có thể dùng lệnh dwarfdump main.o để xem nó có gì bên trong. Dưới đây là phần dữ liệu sau khi dump

main.o:	file format Mach-O 64-bit x86-64

.debug_info contents:
0x00000000: Compile Unit: length = 0x000001ff, format = DWARF32, version = 0x0004, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x00000203)

0x0000000b: DW_TAG_compile_unit
              DW_AT_producer	("Apple Swift version 5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)")
              DW_AT_language	(DW_LANG_Swift)
              DW_AT_name	("main.swift")
              DW_AT_LLVM_sysroot	("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk")
              DW_AT_APPLE_sdk	("MacOSX11.3.sdk")
              DW_AT_stmt_list	(0x00000000)
              DW_AT_comp_dir	("/Users/thanhvv/Desktop/yyy")
              DW_AT_APPLE_major_runtime_vers	(0x05)
              DW_AT_low_pc	(0x0000000000000000)
              DW_AT_high_pc	(0x0000000000000446)

0x00000033:   DW_TAG_module
                DW_AT_name	("main")

0x00000038:     DW_TAG_subprogram
                  DW_AT_low_pc	(0x0000000000000000)
                  DW_AT_high_pc	(0x000000000000004b)
                  DW_AT_frame_base	(DW_OP_reg6 RBP)
                  DW_AT_linkage_name	("main")
                  DW_AT_name	("main")
                  DW_AT_decl_file	("/Users/thanhvv/Desktop/yyy/main.swift")
                  DW_AT_decl_line	(1)
                  DW_AT_type	(0x0000014c "$ss5Int32VD")
                  DW_AT_external	(true)

0x00000055:     DW_TAG_subprogram
                  DW_AT_low_pc	(0x0000000000000050)
                  DW_AT_high_pc	(0x000000000000033d)
                  DW_AT_frame_base	(DW_OP_reg6 RBP)
                  DW_AT_linkage_name	("$s4main5hello4nameySS_tF")
                  DW_AT_name	("hello")
                  DW_AT_decl_file	("/Users/thanhvv/Desktop/yyy/main.swift")
                  DW_AT_decl_line	(3)
                  DW_AT_type	(0x000001cf "$sytD")
                  DW_AT_external	(true)

0x00000072:       DW_TAG_formal_parameter
                    DW_AT_location	(DW_OP_fbreg -32)
                    DW_AT_name	("name")
                    DW_AT_decl_file	("/Users/thanhvv/Desktop/yyy/main.swift")
                    DW_AT_decl_line	(3)
                    DW_AT_type	(0x000001e9 "const $sSSD")

0x00000080:       DW_TAG_lexical_block
                    DW_AT_ranges	(0x00000000
                       [0x0000000000000078, 0x00000000000000a9)
                       [0x00000000000000b9, 0x000000000000033d))

0x00000085:         DW_TAG_variable
                      DW_AT_location	(DW_OP_fbreg -16, DW_OP_deref)
                      DW_AT_name	("today")
                      DW_AT_decl_file	("/Users/thanhvv/Desktop/yyy/main.swift")
                      DW_AT_decl_line	(4)
                      DW_AT_type	(0x000001e4 "const $s10Foundation4DateVD")

0x00000094:         DW_TAG_variable
                      DW_AT_location	(DW_OP_fbreg -48)
                      DW_AT_name	("$interpolation")
                      DW_AT_type	(0x0000017d "$sSS19StringInterpolationaD")
                      DW_AT_artificial	(true)

0x000000a0:         NULL

0x000000a1:       NULL

0x000000a2:     DW_TAG_subprogram
                  DW_AT_low_pc	(0x0000000000000340)
                  DW_AT_high_pc	(0x0000000000000390)
                  DW_AT_APPLE_omit_frame_ptr	(true)
                  DW_AT_frame_base	(DW_OP_reg7 RSP)
                  DW_AT_linkage_name	("$s10Foundation4DateVACs23CustomStringConvertibleAAWl")
                  DW_AT_artificial	(true)
                  DW_AT_external	(true)

0x000000b5:     DW_TAG_subprogram
                  DW_AT_low_pc	(0x0000000000000390)
                  DW_AT_high_pc	(0x00000000000003b2)
                  DW_AT_frame_base	(DW_OP_reg6 RBP)
                  DW_AT_linkage_name	("$ss26DefaultStringInterpolationVWOh")
                  DW_AT_artificial	(true)
                  DW_AT_external	(true)

0x000000c8:     DW_TAG_subprogram
                  DW_AT_low_pc	(0x0000000000000440)
                  DW_AT_high_pc	(0x0000000000000446)
                  DW_AT_frame_base	(DW_OP_reg6 RBP)
                  DW_AT_linkage_name	("$sSa12_endMutationyyF")
                  DW_AT_type	(0x000001cf "$sytD")
                  DW_AT_artificial	(true)
                  DW_AT_external	(true)

0x000000df:     NULL

0x000000e0:   DW_TAG_imported_module
                DW_AT_import	(0x00000033)

0x000000e5:   DW_TAG_module
                DW_AT_name	("Swift")
                DW_AT_LLVM_include_path	("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/usr/lib/swift/Swift.swiftmodule/x86_64-apple-macos.swiftinterface")

0x000000ee:     DW_TAG_subprogram
                  DW_AT_low_pc	(0x00000000000003c0)
                  DW_AT_high_pc	(0x00000000000003f7)
                  DW_AT_frame_base	(DW_OP_reg6 RBP)
                  DW_AT_linkage_name	("$ss27_finalizeUninitializedArrayySayxGABnlF")
                  DW_AT_name	("_finalizeUninitializedArray")
                  DW_AT_type	(0x00000157 "structure ")
                  DW_AT_external	(true)

0x00000109:       DW_TAG_variable
                    DW_AT_location	(DW_OP_fbreg -16)
                    DW_AT_name	("$\317\204_0_0")
                    DW_AT_type	(0x000001f3 "Element")
                    DW_AT_artificial	(true)

0x00000115:       NULL

0x00000116:     DW_TAG_subprogram
                  DW_AT_low_pc	(0x0000000000000400)
                  DW_AT_high_pc	(0x000000000000041c)
                  DW_AT_frame_base	(DW_OP_reg6 RBP)
                  DW_AT_linkage_name	("$ss5print_9separator10terminatoryypd_S2StFfA0_")
                  DW_AT_name	("print")
                  DW_AT_type	(0x00000172 "$sSSD")
                  DW_AT_external	(true)

0x00000131:     DW_TAG_subprogram
                  DW_AT_low_pc	(0x0000000000000420)
                  DW_AT_high_pc	(0x000000000000043c)
                  DW_AT_frame_base	(DW_OP_reg6 RBP)
                  DW_AT_linkage_name	("$ss5print_9separator10terminatoryypd_S2StFfA1_")
                  DW_AT_name	("print")
                  DW_AT_type	(0x00000172 "$sSSD")
                  DW_AT_external	(true)

0x0000014c:     DW_TAG_structure_type
                  DW_AT_name	("Int32")
                  DW_AT_linkage_name	("$ss5Int32VD")
                  DW_AT_byte_size	(0x04)
                  DW_AT_APPLE_runtime_class	(DW_LANG_Swift)

0x00000157:     DW_TAG_structure_type
                  DW_AT_byte_size	(0x08)
                  DW_AT_APPLE_runtime_class	(DW_LANG_Swift)

0x0000015a:       DW_TAG_member
                    DW_AT_type	(0x00000161 "$sSayxGD")
                    DW_AT_data_member_location	(0x00)

0x00000160:       NULL

0x00000161:     DW_TAG_structure_type
                  DW_AT_name	("Array")
                  DW_AT_linkage_name	("$sSayxGD")
                  DW_AT_byte_size	(0x00)
                  DW_AT_APPLE_runtime_class	(DW_LANG_Swift)

0x0000016c:       DW_TAG_template_type_parameter
                    DW_AT_type	(0x000001da "$sxD")

0x00000171:       NULL

0x00000172:     DW_TAG_structure_type
                  DW_AT_name	("String")
                  DW_AT_linkage_name	("$sSSD")
                  DW_AT_byte_size	(0x10)
                  DW_AT_APPLE_runtime_class	(DW_LANG_Swift)

0x0000017d:     DW_TAG_typedef
                  DW_AT_type	(0x00000186 "$ss26DefaultStringInterpolationVD")
                  DW_AT_name	("$sSS19StringInterpolationaD")

0x00000186:     DW_TAG_structure_type
                  DW_AT_name	("DefaultStringInterpolation")
                  DW_AT_linkage_name	("$ss26DefaultStringInterpolationVD")
                  DW_AT_byte_size	(0x10)
                  DW_AT_APPLE_runtime_class	(DW_LANG_Swift)

0x00000191:     NULL

0x00000192:   DW_TAG_imported_module
                DW_AT_import	(0x000000e5)

0x00000197:   DW_TAG_module
                DW_AT_name	("Foundation")
                DW_AT_LLVM_include_path	("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework")

0x000001a0:   DW_TAG_imported_module
                DW_AT_import	(0x00000197)

0x000001a5:   DW_TAG_module
                DW_AT_name	("Foundation")
                DW_AT_LLVM_include_path	("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/usr/lib/swift/Foundation.swiftmodule/x86_64-apple-macos.swiftinterface")

0x000001ae:     DW_TAG_structure_type
                  DW_AT_name	("Date")
                  DW_AT_linkage_name	("$s10Foundation4DateVD")
                  DW_AT_byte_size	(0x40)
                  DW_AT_APPLE_runtime_class	(DW_LANG_Swift)

0x000001b9:     NULL

0x000001ba:   DW_TAG_imported_module
                DW_AT_decl_file	("/Users/thanhvv/Desktop/yyy/main.swift")
                DW_AT_decl_line	(1)
                DW_AT_import	(0x000001a5)

0x000001c1:   DW_TAG_module
                DW_AT_name	("SwiftOnoneSupport")
                DW_AT_LLVM_include_path	("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/usr/lib/swift/SwiftOnoneSupport.swiftmodule/x86_64-apple-macos.swiftinterface")

0x000001ca:   DW_TAG_imported_module
                DW_AT_import	(0x000001c1)

0x000001cf:   DW_TAG_structure_type
                DW_AT_name	("$sytD")
                DW_AT_linkage_name	("$sytD")
                DW_AT_byte_size	(0x00)
                DW_AT_APPLE_runtime_class	(DW_LANG_Swift)

0x000001da:   DW_TAG_structure_type
                DW_AT_name	("$sxD")
                DW_AT_linkage_name	("$sxD")
                DW_AT_declaration	(true)
                DW_AT_APPLE_runtime_class	(DW_LANG_Swift)

0x000001e4:   DW_TAG_const_type
                DW_AT_type	(0x000001ae "$s10Foundation4DateVD")

0x000001e9:   DW_TAG_const_type
                DW_AT_type	(0x00000172 "$sSSD")

0x000001ee:   DW_TAG_module
                DW_AT_name	("Builtin")

0x000001f3:     DW_TAG_typedef
                  DW_AT_type	(0x000001fd "$sBpD")
                  DW_AT_name	("Element")

0x000001fc:     NULL

0x000001fd:   DW_TAG_pointer_type
                DW_AT_name	("$sBpD")

0x00000202:   NULL
0x00000203: Compile Unit: length = 0x00000026, format = DWARF32, version = 0x0004, abbr_offset = 0x0000, addr_size = 0x08 (next unit at 0x0000022d)

0x0000020e: DW_TAG_compile_unit
              DW_AT_producer	("Apple Swift version 5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)")
              DW_AT_language	(DW_LANG_ObjC)
              DW_AT_name	("Foundation")
              DW_AT_stmt_list	(0x000000be)
              DW_AT_comp_dir	("/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework")
              DW_AT_APPLE_optimized	(true)
              DW_AT_GNU_dwo_id	(0x0000002b00000037)
              DW_AT_GNU_dwo_name	("/var/folders/43/2fwp5g2d02zct4jd4df6s3k80000gn/C/clang/ModuleCache/1UW6XBNV5FNIW/Foundation-2FJBXN8U6QRTS.pcm")

Đây là nội dung DWARF mô tả dữ liệu Debugging. Ở đây có rất nhiều thứ, từ định nghĩa các type tới định nghĩa module, parameter, biến.

Ở dòng 32 bạn sẽ thấy nó có chứa thông tin về function hello. Dòng 43 là thông tin về param name. Dòng 55 là thông tin về biến today.

Như vậy chúng ta đã thấy DWARF chứa tất cả thông tin cần thiết như variable, function, parameter được định nghĩa ở file nào, ở dòng nào và ở địa chỉ nào trong binary.

3. Debugger sử dụng DWARF như thế nào

Sau khi compile xong chương trình ở phần 2. Thì ta có 1 binary có tên main. Trong main sẽ chứa thông tin về debug map mô tả những nơi nào có chứa dữ liệu DWARF.

Khi chúng ta đặt breakpoint vào một dòng nào đó ở code(ví dụ đặt ở dòng số 5 ở chương trình bên trên). Thì debugger sẽ sử dụng dữ liệu DWARF kia để xác định xem dòng đó tương ứng với địa chỉ bao nhiêu trong executable file. Và từ đó thì debugger sẽ thực hiện dừng tại địa chỉ đó khi chương trình chạy tới đó.

Khi chương trình chạy tới dòng ta đặt breakpoint đó. Tiếp tục, nó sẽ truy suất ngược lên để tìm các biến có trong function đó(ở đây là function hello). Thì nó sẽ tìm được ở trong function hello này có biến today có kiểu Foundation.Date. Sau khi có địa chỉ thì debugger sẽ tiến hành đọc địa chỉ đó để lấy ra giá trị của biến today đó.

4. Generate dSYM

Ở phần trên thì mình có nói là debugger sẽ lấy dữ liệu từ trong file main.o. File main.o bên trên là object file nhưng có chứa thông tin DWARF. Ví dụ app của chúng ta được đăng lên appstore và dùng firebase crashlytics để report crash. Nhưng khi xảy ra crash thì ta nhận được chỉ là report chứa toàn địa chỉ, lúc này ta cần thông tin DWARF để xem những địa chỉ đó là gì, function nào bị crash.

Vì vậy ta cần gen ra dSYM file chỉ chứa thông tin DWARF. dSYM viết tắt của Debug Symbol, nó chứa thông tin DWARF bên trong để phục vụ mục đích debug và symbolicate các địa chỉ của crash report về sau.

Để gen dSYM file thì ta có thể dùng công dùng dsymutil có sẵn ở trong hệ thống. Thì cách thức hoạt động của nó tương tự, nó sẽ lấy thông tin debug map bên trong executable file. Sau đó nó truy suất những file có chứa DWARF( ở đây là main.o) và gen ra dSYM riêng lẻ.

dsymutl main

Tuy nhiên thực tế chúng ta không cần phải làm như vậy. Chúng ta chỉ cần vào Xcode setting và chọn DWARF with dSYM file ở phần Debug Infomation Format là nó sẽ gen ra dSYM sau khi compile app.

Bật DWARF with dSYM

Vậy nên lúc chúng ta sử dụng firebase crashlytics mà muốn crashlytics phân tích crash ở chế độ DEBUG thì phải bật chọn DWARF with dSYM file ở cả chế độ DEBUG.

5. Tổng kết

Như vậy là có dữ liệu về debug thì debugger khá dễ dàng để lấy được các thông tin để phục vụ chúng ta debug phải không nào.

Learn more:
https://en.wikipedia.org/wiki/DWARF
http://dwarfstd.org/doc/DWARF5.pdf
http://www.dwarfstd.org/doc/Debugging%20using%20DWARF-2012.pdf

Leave a Reply