-
Notifications
You must be signed in to change notification settings - Fork 4
LLDB
通过 LLDB
我们可以对 App 进行动态调试,LLDB
内置于 Xcode
中,在真机运行后,会在手机上部署 Debug Server
,通过 LLDB -- debugserver
建立通信。 新的 iPhone 在没有进行过真机调试前,手机上是不会存在 debugserver
-
debugserver
路径-
Mac Xcode 下存在路径
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/15.2/DeveloperDiskImage.dmg/usr/bin/debugserver
-
iPhone 下路径
Develop/usr/bin
-
任何命令行工具类的工具都需要这样一个帮助入口, 帮助我们快速熟悉一个新的指令, 使用格式如下:
help expression
help breakpoint
LLDB 包含很多工具,区别与我们使用的某些固定工具直接 xxx -h/--help
可以使用print
、prin
、pri
或者p
, 但是不能使用pr
, 因为LLDB
不能消除和process
的歧义
p/po
po
: 输出值 或者 对象的地址
p
: 输出值 + 值类型 + 引用名 + 内存地址
通过打印相同的实例对象p
对比
(lldb) p p
(Person *) $5 = 0x0000000100550b50
(lldb) po p
<Person: 0x100550b50>
结果中有个$5
, 任何以$
符开头的东西都是存在于LLDB
的命名空间的, 它们是为了帮助我们进行调试而存在的.
除此之外, p
还支持进制转换, 默认p
是十进制打印
-
p/t
二进制打印
e.g.
(lldb) p/t p (Person *) $1 = 0b0000000000000000000000000000000100000000010101010000101101010000
-
p/c
数字转十进制字符
(lldb) p/c 66 (int) $9 = B\0\0\0
-
p/c
打印以空中指的字符串(以
\0
结尾的字符串) -
p/x
十六进制打印
(lldb) p/x 100 (int) $7 = 0x00000064
(lldb) p/x (float)100.0 (float) $9 = 0x42c80000 (lldb) p/x (double)100.0 (double) $10 = 0x4059000000000000
-
p/o
八进制打印
(lldb) p/o 100 (int) $6 = 0144
-
p/d
字符转十进制数字
(lldb) p/d 'A' (char) $8 = 65
在运行时修改环境中的某个值, 这非常有用, 配合断点调试, 帮助我们 mock
一些场景
e.g.
expression count = 18
通常简写为e
, 我的习惯是expr
如果设置了选项,可以通过
--
连接表达式,--
作为分隔,如果没有选项--
可以省略
- e.g. 比如常见的
NSLog
打印对象信息,等同于expression -O -- 对象
, 一般直接使用po 对象
即可- e.g.
expression -- self.view.backgroundColor = [UIColor redColor]
这里的--
可以省略
除了完整的使用 expression
命令,还可以用 call
、print
、p
替代,效果是一样的
通过执行以下命令, 可以在控制台快速查看视图层级
(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
一些断点调试场景下, 可以在LLDB
环境下完成
e.g. 例如在断点下通过在控制台下同构LLDB
的expression
指令调整了某个UIView
控件的背景色, 直接执行一下指令来刷新UI
(lldb) e (void)[CATransaction flush]
补充: 只有程序在继续运行之后才会看到界面的变化. 因为改变的内容必须被发送到渲染服务中,然后显示才会被更新. 渲染服务实际上是一个另外的进程(被称作backboardd
).这就是说即使我们正在调试的内容所在的进程被打断了, backboardd
也是继续运行着的. 因此我们在断点情况下通过LLDB
执行上面的命令,实现了UI的更新操作.
查看当前对象的内存情况, 简写x
e.g. p
是实例化的一个对象指针
(lldb) x p
0x100550b50: 8d 22 00 00 01 80 1d 00 78 10 00 00 01 00 00 00 ."......x.......
0x100550b60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Tips:
- iOS 采用小端存储
- 上面输出结果中,最左侧一列显示的是内存地址,右侧是十六进制数据 (也就是每一小段2个十六进制就是1 字节),每一行地址从左至右递增
- 那么比如
22
这组值的地址就是0x100550b50 + 1
即0x100550b51
LLDB 现在支持 GDB 速记格式语法,但是命令后不能有空格 GDB 到 LLDB 命令映射
-
x/4xg
(lldb) x/4xg p 0x100550b50: 0x001d80010000228d 0x0000000100001078 0x100550b60: 0x0000000000000000 0x0000000000000000
完整的lldb命令是:
memory read --size 8 --format x --count 4 p
参数解读:
从地址 p上 读取内存并显示 4 个8字节(g) 以十六进制(x) 展示
按照GDB的格式:
x/nfu addr
(memory read/个数+格式+每一个的字节数 内存地址
)GDB - Examining Memory - x/nfu addr
n, f, and u are all optional parameters that specify how much memory to display and how to format it; addr is an expression giving the address where you want to start displaying memory. If you use defaults for nfu, you need not type the slash ‘/’. Several commands set convenient defaults for addr. n, the repeat count The repeat count is a decimal integer; the default is 1. It specifies how much memory (counting by units u) to display. If a negative number is specified, memory is examined backward from addr. f, the display format The display format is one of the formats used by print (‘x’, ‘d’, ‘u’, ‘o’, ‘t’, ‘a’, ‘c’, ‘f’, ‘s’), ‘i’ (for machine instructions) and ‘m’ (for displaying memory tags). The default is ‘x’ (hexadecimal) initially. The default changes each time you use either x or print. u, the unit size The unit size is any of b Bytes. h Halfwords (two bytes). w Words (four bytes). This is the initial default. g Giant words (eight bytes).
-
4
: the repeat count- 打印4段, 否则需要肉眼分段查看, 对应全名参数为
--count 4
- 打印4段, 否则需要肉眼分段查看, 对应全名参数为
-
x
: the display format-
每一段的打印格式,以十六进制打印, 对应全名参数为
--format x
lldb --format 支持的格式有
"default" 'B' or "boolean" 'b' or "binary" 'y' or "bytes" 'Y' or "bytes with ASCII" 'c' or "character" 'C' or "printable character" 'F' or "complex float" 's' or "c-string" 'd' or "decimal" 'E' or "enumeration" 'x' or "hex" 'X' or "uppercase hex" 'f' or "float" 'o' or "octal" 'O' or "OSType" 'U' or "unicode16" "unicode32" 'u' or "unsigned decimal" 'p' or "pointer" "char[]" "int8_t[]" "uint8_t[]" "int16_t[]" "uint16_t[]" "int32_t[]" "uint32_t[]" "int64_t[]" "uint64_t[]" "float16[]" "float32[]" "float64[]" "uint128_t[]" 'I' or "complex integer" 'a' or "character array" 'A' or "address" "hex float" 'i' or "instruction" 'v' or "void" 'u' or "unicode8"
比如小写十六进制阅读不舒服,想要换成大写,就可以写成
memory read --size 8 --format X --count 4 obj
但是要注意 lldb的命令参数和gdb的参数并不通用
-
-
g
: Words (four bytes). This is the initial default.- 每一段的大小 4个字节, 等同于
--size 8
- 对应参数
--size <byte-size>
- b 1个字节
- h 2个字节
- w 4个字节
- g 8个字节
- 每一段的大小 4个字节, 等同于
-
iOS为小端存储模式, 数据的高字节存储在高地址中,数据的低字节存储在低地址
对比x
与x/4xg
结果,x/4xg
是按照正常的书写格式输出, 低位在右, 高位在左, 阅读和使用更加方便
-
thread backtrace
查看线程和堆栈框架描述的信息
也可以使用
bt
查看线程信息在结果中可以看到很多
frame
, 代表一帧,一般一帧就代表一个函数调用 -
thread backtrace all
可以使用bt all
-
thread return
让函数直接返回某个值,不会执行断点后面的代码
-
thread continue、 continue、 c
程序继续运行
-
thread step-over、next、n
单步运行,把子函数当做整体一步执行
-
thread step-in、step、s
单步运行,遇到子函数会进入子函数
-
thread step-out、finish
直接执行完当前函数的所有代码,返回到上一个函数
-
thread step-inst-over、nexti、ni
汇编指令,执行一句汇编指令
-
thread step-inst、stepi、si
汇编指令
-
si、ni
和s、n
类似-
s、n
是源码级别 -
si、ni
是汇编指令级别
-
-
frame variable
打印当前栈帧的变量, 不指定变量就是打印全部
代码断点
-
breakpoint set
设置断点
-
breakpoint set -a
+ 函数地址 -
breakpoint set -n
+ 函数名
e.g.
breakpoint set -n test
breakpoint set -n touchedBegan:withEvent:
breakpoint set -n "-[ViewController touchesBegan:withEvent:]"
-
-
breakpoint list
列出所有的断点(每个断点都有自己的编号)
-
breakpoint disable
+ 断点编号禁用断点
-
breakpoint enable
+ 断点编号启用断点
-
breakpoint delete
+ 断点编号删除断点
-
breakpoint command add
+ 断点编号给断点预先设置需要执行的命令,到触发断点时,就会按顺序执行
-
breakpoint command list
+ 断点编号查看某个断点设置的命令
-
breakpoint command delete
+ 断点编号删除某个断点设置的命令
内存断点,在内存数据发生改变的时候触发
-
watchpoint set variable
+ 变量e.g.
watchpoint set variable self->_age
这里需要注意是直接设置的成员变量,而不是
self.age
,当值改变时就会断到属性声明处 -
watchpoint set expression
+ 地址e.g.
watchpoint set expression &(self->_age)
除此之外,与 breakpoint
一样,下面的指令具有相似的功能
watchpoint list
-
watchpoint disable
+ 断点编号 -
watchpoint enable
+ 断点编号 -
watchpoint delete
+ 断点编号 -
watchpoint command add
+ 断点编号 -
watchpoint command list
+ 断点编号 -
watchpoint command delete
+ 断点编号
-
image lookup
模块查找
-
image lookup -t
+ 类型查找某个类型的信息
e.g.
image lookup -t NSInteger
, 就会显示占用大小,所在头文件等信息 -
image lookup -a
+ 地址根据内存地址查找在模块中的位置,可以在崩溃时用来快速定位
-
image lookup -n
+ 符号或者函数名查找某个符号或者函数的位置
-
-
image list
列出所加载的模块信息
-
image list -o -f
打印出模块的偏移地址、全路径
-
ShenYj.github.io - 简书地址 - 返回首页
-
Apple
Common
蓝牙
LBS
音视频
- AVAudioSessionchange_route
- 切换线路
- StreamingKit
- Audio Unit 基础
OC 与 Swift 混编
Object-C
- 代码混淆
- autoreleasepool
- 忽略编译器(clang)警告
- 定时器
- 锁
- RunLoop
- block
- NS_REFINED_FOR_SWIFT
- NS_CLOSED_ENUM
- NS_TYPED_ENUM、NS_STRING_ENUM
- NS_TYPED_EXTENSIBLE_ENUM、NS_EXTENSIBLE_STRING_ENUM
- 关键字nonnull和nullable
- class、objc_getClass和object_getclass方法区别
- isKindOfClass和isMemberOfClass
- 应用程序的加载
- non-lazy classes & lazy classes
- load方法
- initialize方法
- 方法的本质
- 类型编码
- self和super
- 类的内存分析
Swift
- precondition
- 权限控制
- Array常用Api
- String初始化、定义
- String常用Api
- String截取演练
- Set定义、创建
- Set访问和修改
- Dictionary操作
- Dictionary和KeyValuePairs
- Dictionary与String转换
- 常用高阶函数
- enum原始值
- enum关联值
- enum遍历
- 递归enum
- enum内存分配
- 指针
- for循环
- break跳出循环
- 变量名与关键字冲突
- 类的定义
- 类的继承和初始化
- 关键字: final
- 关键字: mutating
- 关键字: lazy
- 修饰类方法的关键字: static
- 关键字: final、dynamic、objc和_dynamicReplacement
- 关键字:@dynamicMemberLookup和@dynamicCallable
- 关键字: propertyWrapper
- 自定义运算符
- 下标: subscript
- 扩展: extension
- 协议: protocol
- 协议和扩展
- 为什么需要泛型
- 泛型函数定义
- 泛型类型
- 泛型的类型约束
- 关联类型
- 为泛型定义要求
- 泛型下标
- 多线程
- Attributes
- 错误处理
- Codable
- DispatchSourceTimer
- Swift 5.x 演练: 更多功能编辑页
- Swift 5.x 类库收集
- 单元测试笔记
- 实例对象内存结构
- 元类型、Type、Self
- frozen
- convention
- Swift(5.3.2)源码编译
- SQLite.Swift类库演练
- Swift 5.5 关键字: async/await
- Swift 5.5 新特性: Continuations
- Swift 5.5 新特性: Actor
- Swift 方法调度
- Swift Mirror
- Swift 关键字: @_silgen_name
- Swift 关键字: @_disfavoredOverload
- swiftmodule
- Swift 5.6 新特性: Type placeholders
- Swift 5.6 新特性: #unavailable
- Swift 5.6 新特性: CodingKeyRepresentable
- Swift 5.6 新特性: existential any
- Swift 5.7 新特性: if-let/guard 语法简化
- Swift 5.7 新特性: Multi-statement closure type inference
- Swift 5.8 新特性: @backDeployed
- Swift 5.9 新特性: if switch expressions
- Swift 6.0 新特性:@preconcurrency
RxSwift
macOS - AppKit
-
iOS Assembly(ARM64)
-
C++
C++ 基础
- cout、cin
- 函数重载
- 默认参数
- extern "C"
- pragma once
- inline function
- const
- Reference
- 汇编
- 类和对象
- 堆空间内存管理
- Constructor
- Destructor
- 成员变量初始化
- 声明与实现分离
- namespace
- 继承
- 访问权限
- 初始化列表
- 多态:虚函数
- 多态:虚函数实现原理
- 多态:虚析构函数
- 多态:纯虚函数
- 多态:抽象类
- 多继承
- static
- static: 单例模式
- const 成员
- 引用类型成员
- 拷贝构造函数
- 调用父类的拷贝构造函数
- 浅拷贝、深拷贝
- 对象型参数和返回值
- 匿名对象
- 隐式构造
- 编译器自动生成的构造函数
- 友元
- 内部类
- 局部类
- 运算符重载
- 模板
- 类型转换
- C++标准
- Lambda
- 异常
- 智能指针
-
Flutter
Dart
Flutter
-
Go
Go 基础
-
Ruby
Ruby 基础
-
React-Native
React-Native
-
工具篇
-
Swift Package Manager
-
自动化
-
TroubleShooting
-
扩展