LLDB-iOS高效开发必备利器详解

LLDB

一、什么是lldb

lldb官网文档地址:https://lldb.llvm.org/

LLDB is a next generation, high-performance debugger. It is built as a set of reusable components which highly leverage existing libraries in the larger LLVM Project, such as the Clang expression parser and LLVM disassembler.

LLDB is the default debugger in Xcode on macOS and supports debugging C, Objective-C and C++ on the desktop and iOS devices and simulator.

lldb是一个高效的debug工具。在大型的LLVM工程中,lldb作为一个可重复使用的组件库被使用,例如作为Clang 解析器或者用于LLVM 反汇编使用。

lldb 是默认被嵌入的Xcode IDE中的,使用Xcode 开发过程中默认支持LLDB调试的,其支持 C、C++、OC语言,同时支持了真机和模拟器。

lldb 支持以下平台:

  • macOS debugging for i386, x86_64 and AArch64

  • iOS, tvOS, and watchOS simulator debugging on i386, x86_64 and AArch64

  • iOS, tvOS, and watchOS device debugging on ARM and AArch64

  • Linux user-space debugging for i386, x86_64, ARM, AArch64, MIPS64, PPC64le, s390x

  • FreeBSD user-space debugging for i386, x86_64, ARM, AArch64, MIPS64, PPC

  • NetBSD user-space debugging for i386 and x86_64

  • Windows user-space debugging for i386, x86_64, ARM and AArch64 (*)

    下面的内容讲解是在ios平台做的内容测试,其他平台没做探究。

在上述介绍了lldb的概念提到了LLVM 和Clang解析器,详细了解下这两个名词。

LLVM:LLVM是构架编译器(compiler)的框架系统,以C++编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time),对开发者保持开放,并兼容已有脚本。

----百度百科

LLVM计划启动于2000年,最初由美国UIUC大学的Chris Lattner博士主持开展。2006年Chris Lattner加盟Apple Inc.并致力于LLVM在Apple开发体系中的应用。Chris Lattner 同时是Swift创造者。

LLVM的使用

开发硬件平台 Windows Linux Mac OS
在Windows上可以使用Mingw编译LLVM
也可以用VisualStudio 编译 GCC编译或者是以LLVM/Clang编译 Xcode自带

传统的编译器架构

传统编译器架构图.jpeg

  1. Frontend:前端

    前端:词法分析、语法分析、语义分析、生成中间代码

  2. Optimizer:优化器

    优化器:中间代码优化

  3. Backend:后端

    后端:生成机目标器码

LLVM编译器架构

LLVM架构图.png

从LLVM架构图我们可以看出这种架构方案的高明之处。

  • 使用统一的中间代码LLVM IR(LLVM Intermediate Representation)
  • 高扩展性:无论是增加一种新的语言,还是新增加了一种新的设备,中间优化器不用做修改,只需要增加一种新的前端和后端即可
  • LLVM目前被作为实现各种静态和运行时编译语言的通用基础结构(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)

明白LLVM之后 那Clang 又是起什么作用呢?

Clang是一个由Apple主导编写,基于LLVM的C/C++/Objective-C编译器

从上面的介绍我们可以看出其实Clang 是基于LLVM框架针对C/C++/Objective-C语言开发出来的一个编译器,不具备通用性和不可移植性。

相比于GCC,Clang具有如下优点

  • 编译速度快:在某些平台上,Clang的编译速度显著的快过GCC(Debug模式下编译OC速度比GGC快3倍)
  • 占用内存小:Clang生成的AST所占用的内存是GCC的五分之一左右
  • 模块化设计:Clang采用基于库的模块化设计,易于 IDE 集成及其他用途的重用
  • 诊断信息可读性强:在编译过程中,Clang 创建并保留了大量详细的元数据 (metadata),有利于调试和错误报告
  • 设计清晰简单,容易理解,易于扩展增强

Clang与LLVM关系

Clang与LLVM.png

Clang与LLVM

LLVM整体架构,前端用的是clang,广义的LLVM是指整个LLVM架构,一般狭义的LLVM指的是LLVM后端(包含代码优化和目标代码生成)。

源代码(c/c++)经过clang–> 中间代码(经过一系列的优化,优化用的是Pass) --> 机器码

二、 lldb调试案例

查看lldb调试都支持哪些指令呢?我们可以通过 help 来查找


截屏20210620 下午7.37.32.png

这里只粘贴了部分命令,还有很多命令可支持。

下面介绍几个常用的指令:

  • expression : 调试过程中修改值 ,可用 e 代替。
    截屏20210620 下午7.42.08.png

还可以调用方法 :(lldb) expression [self testAA] 等同于call的功能

还可以输入命令:所以 expression 的指令包含了p&call的集合

po = **e -O – **

案例修改view颜色

(lldb) po self.view
>

(lldb) e id $myView = (id)0x600034cfbe90
(lldb) e (void)[$myView setBackgroundColor:[UIColor blueColor]]

  • **p/po **: 输出(print的简写)
截屏20210620 下午11.27.29.png
截屏20210620 下午7.49.19.png

可以看出p 和po都是输出内容的命令,区别是

p 输出包含了数据类型,object类型输出的是对象的指针地址+对象类型,基本类型输出的是 基本类型+值

po输出的是值

(lldb) p name
(NSString *) $0 = 0x000000010d2e2410
(lldb) po name
miqiyy

这里的 0 表示p/po第一条输出,$2表示 第三条输出

p还能控制输出的格式类型: p/

(lldb) po/t 16
0b00000000000000000000000000010000

(lldb) po/x 16
0x00000010

  • c、n、s 代替continue,step over,step into,step out

    c = coutinue :直接跳过 改 断点

    n = step over:一步步跟踪

    s = step into:调入方法内

    当我们不经意见进入一个本不想进入的方法内,可以点击 step out 直接跳出 返回到 调用位置

  • thread return [可选的返回参数]

thread return 绕过方法的真正执行,伪造返回值

断点相关

  • br list:显示工程中所有的断点

    (lldb) br list
    Current breakpoints:
    1: file = '/Users/xianliangdev/Desktop/LearniOS/LearniOS/ViewController.m', line = 60, exact_match = 0, locations = 1, resolved = 1, hit count = 1
    
      1.1: where = LearniOSA`-[ViewController btnAction] + 123 at ViewController.m:60:17, address = 0x0000000108662d3b, resolved, hit count = 1 
    
    5: file = '/Users/xianliangdev/Desktop/LearniOS/LearniOS/ViewController.m', line = 74, exact_match = 0, locations = 1, resolved = 1, hit count = 0
    
      5.1: where = LearniOSA`-[ViewController testBB] + 23 at ViewController.m:74:9, address = 0x0000000108662e87, resolved, hit count = 0 
    
    
  • br disable/enable

    (lldb) br disable 1
    1 breakpoints disabled.
    (lldb) br list
    Current breakpoints:
    1: file = '/Users/xianliangdev/Desktop/LearniOS/LearniOS/ViewController.m', line = 60, exact_match = 0, locations = 1 Options: disabled 
    
    1.1: where = LearniOSA`-[ViewController btnAction] + 123 at ViewController.m:60:17, address = 0x0000000108662d3b, unresolved, hit count = 1 
      (lldb) br enable 1
    1 breakpoints enabled.
    
    
  • *br set -f .m -l

    br delete

    用命令下断点/删除断点 。

    不过开发过程中还是不用这样做,br set 断点后 没有标识 ,最后调试过程比较麻烦 不能肉眼观察 都有哪些断点了

  • 在调试过程中仍然继续执行 渲染服务,渲染服务实际上是一个另外的进程 (被称作 backboardd)。

    e (void)[CATransaction flush]

  • bt all :打印当前运行的所有线程 运行信息

    * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
      * frame #0: 0x000000010dbe2c39 LearniOSA`-[ViewController btnAction](self=0x00007faba2c09c10, _cmd="btnAction") at ViewController.m:66:17
        frame #1: 0x0000000111eb0937 UIKitCore`-[UIApplication sendAction:to:from:forEvent:] + 83
        frame #2: 0x00000001117d145d UIKitCore`-[UIControl sendAction:to:forEvent:] + 223
        frame #3: 0x00000001117d1780 UIKitCore`-[UIControl _sendActionsForEvents:withEvent:] + 332
        frame #4: 0x00000001117d007f UIKitCore`-[UIControl touchesEnded:withEvent:] + 500
        frame #5: 0x0000000111eecd01 UIKitCore`-[UIWindow _sendTouchesForEvent:] + 1287
        frame #6: 0x0000000111eeeb8c UIKitCore`-[UIWindow sendEvent:] + 4792
        frame #7: 0x0000000111ec8c89 UIKitCore`-[UIApplication sendEvent:] + 596
        frame #8: 0x0000000111f5b7ba UIKitCore`__processEventQueue + 17124
        frame #9: 0x0000000111f51560 UIKitCore`__eventFetcherSourceCallback + 104
        frame #10: 0x000000010e886ede CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
        frame #11: 0x000000010e886dd6 CoreFoundation`__CFRunLoopDoSource0 + 180
        frame #12: 0x000000010e88629e CoreFoundation`__CFRunLoopDoSources0 + 242
        frame #13: 0x000000010e8809f7 CoreFoundation`__CFRunLoopRun + 875
        frame #14: 0x000000010e8801a7 CoreFoundation`CFRunLoopRunSpecific + 567
        frame #15: 0x000000011a483d85 GraphicsServices`GSEventRunModal + 139
        frame #16: 0x0000000111eaa4df UIKitCore`-[UIApplication _run] + 912
        frame #17: 0x0000000111eaf39c UIKitCore`UIApplicationMain + 101
        frame #18: 0x000000010dbe4ca2 LearniOSA`main(argc=1, argv=0x00007ffee201fc60) at main.m:17:12
        frame #19: 0x000000010fc81bbd libdyld.dylib`start + 1
      thread #3
        frame #0: 0x000000011003c53e libsystem_kernel.dylib`__workq_kernreturn + 10
        frame #1: 0x00000001100bd4fd libsystem_pthread.dylib`_pthread_wqthread + 414
        frame #2: 0x00000001100bc467 libsystem_pthread.dylib`start_wqthread + 15
      thread #6, name = 'com.apple.uikit.eventfetch-thread'
        frame #0: 0x000000011003ae7e libsystem_kernel.dylib`mach_msg_trap + 10
        frame #1: 0x000000011003b1f0 libsystem_kernel.dylib`mach_msg + 60
        frame #2: 0x000000010e8864f9 CoreFoundation`__CFRunLoopServiceMachPort + 316
        frame #3: 0x000000010e880b9c CoreFoundation`__CFRunLoopRun + 1296
        frame #4: 0x000000010e8801a7 CoreFoundation`CFRunLoopRunSpecific + 567
        frame #5: 0x000000010df76204 Foundation`-[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 209
        frame #6: 0x000000010df76473 Foundation`-[NSRunLoop(NSRunLoop) runUntilDate:] + 72
        frame #7: 0x0000000111f62ccd UIKitCore`-[UIEventFetcher threadMain] + 464
        frame #8: 0x000000010df9e8a9 Foundation`__NSThread__start__ + 1068
        frame #9: 0x00000001100c0950 libsystem_pthread.dylib`_pthread_start + 224
        frame #10: 0x00000001100bc47b libsystem_pthread.dylib`thread_start + 15
    
    
  • thread all: 展示当前的所有线程列表

    (lldb) thread list
    Process 6862 stopped
    * thread #1: tid = 0x67140, 0x00000001055aebf9 LearniOSA`-[ViewController btnAction](self=0x00007fe214008aa0, _cmd="btnAction") at ViewController.m:63:28, queue = 'com.apple.main-thread', stop reason = step over
      thread #2: tid = 0x67c9d, 0x00000001078ef53e libsystem_kernel.dylib`__workq_kernreturn + 10
      thread #6: tid = 0x67cc4, 0x00000001078ede7e libsystem_kernel.dylib`mach_msg_trap + 10, name = 'com.apple.uikit.eventfetch-thread'
    
    
  • image lookup

    1. image lookup -name 简写 image lookup -n 这个命令在开发过程中 还是比较常用的一个指令,

      常用来使用的范围:

      一、方法的信息:如例子展示了方法btnAction 都在哪些类中实现,并且列出了在对应文件的位置

      2 matches found in /Users/xianliangdev/Library/Developer/Xcode/DerivedData/LearniOS-fpcguxeksnmswvcyseiyshympabj/Build/Products/Debug-iphonesimulator/LearniOSA.app/LearniOSA:
            Address: LearniOSA[0x0000000100004bb0] (LearniOSA.__TEXT.__text + 3904)
            Summary: LearniOSA`-[ViewController btnAction] at ViewController.m:59        Address: LearniOSA[0x0000000100006bb0] (LearniOSA.__TEXT.__text + 12096)
            Summary: LearniOSA`-[Person btnAction] at Person.m:19
      
      

      二、 查找方法的实现,特别是在引入三方sdk中 category 引用了同一个方法的实现,这是灾难性的 可能是因此引入一些bug

    2. image lookup -adress 简写 image lookup -a:如下例子 数组越界crash

      2021-06-27 23:00:16.056032+0800 LearniOSA[7276:457801] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** __boundsFail: index 4 beyond bounds [0 .. 2]'
      *** First throw call stack:
      (
          0   CoreFoundation                      0x000000010f575fba __exceptionPreprocess + 242
          1   libobjc.A.dylib                     0x000000010f41fff5 objc_exception_throw + 48
          2   CoreFoundation                      0x000000010f5f4523 _CFThrowFormattedException + 194
          3   CoreFoundation                      0x000000010f5fc2c4 -[__NSArrayI getObjects:range:].cold.1 + 0
          4   CoreFoundation                      0x000000010f59758e -[__NSArrayI objectAtIndex:] + 38
          5   LearniOSA                           0x000000010ebea5ee -[ViewController btnAction] + 321
          **
      )
      libc++abi: terminating with uncaught exception of type NSException
      *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** __boundsFail: index 4 beyond bounds [0 .. 2]'
      terminating with uncaught exception of type NSException
      CoreSimulator 757.5 - Device: iPhone 12 (AE147971-403C-4E70-A47A-42DAC6A13884) - Runtime: iOS 14.5 (18E182) - DeviceType: iPhone 12
      (lldb) image lookup -a 0x000000010ebea5ee
      LearniOSA was compiled with optimization - stepping may behave oddly; variables may not be available.
            Address: LearniOSA[0x00000001000025ee] (LearniOSA.__TEXT.__text + 2500)
            Summary: LearniOSA`-[ViewController btnAction] + 321 at ViewController.m:70:26
      
      
    3. image lookup -type <类型> 简写 image lookup -t <类型>

      (lldb) image lookup -type Person
      Best match found in /Users/xianliangdev/Library/Developer/Xcode/DerivedData/LearniOS-fpcguxeksnmswvcyseiyshympabj/Build/Products/Release-iphonesimulator/LearniOSA.app/LearniOSA:
      id = {0x7fffffff0004bd90}, name = "Person", byte-size = 16, decl = Person.h:13, compiler_type = "@interface Person : NSObject{
          (anonymous struct) mySelf;
          char _my;
      }
      @end"
      
      
三、LLDB 和Python

lldb支持引入 脚本

(lldb) script import os
(lldb) script os.system("open http://www.objc.io/")
0

通过引入python 直接打开了链接

这样就允许你创造各种酷的命令。把下面的语句放到文件 ~/myCommands.py 中:

def caflushCommand(debugger, command, result, internal_dict): debugger.HandleCommand("e (void)[CATransaction flush]")

然后再 LLDB 中运行:

command script import ~/myCommands.py

参考资料:

1、LLVM:https://baike.baidu.com/item/LLVM/3598690?fr=aladdin

2、Clang:https://baike.baidu.com/item/clang/3698345?fr=aladdin

3、理解LLVM:https://www.cnblogs.com/wuhh123/p/10668609.html

你可能感兴趣的:(LLDB-iOS高效开发必备利器详解)