# GDB/LLDB调试Native

## 概述

GDB和LLDB是调试Native代码（C/C++）的强大工具。在Android开发中，特别是Framework层和NDK开发，掌握这些调试工具至关重要。

## GDB基础

### GDB简介

GDB (GNU Debugger) 是Linux/Unix系统上最常用的调试器，支持C、C++、Java等多种语言。

### 安装GDB

```bash
# Ubuntu/Debian
sudo apt-get install gdb

# 检查版本
gdb --version
```

### 基本用法

```bash
# 启动GDB
gdb program

# 或者附加到运行中的进程
gdb program <PID>

# 加载core文件
gdb program core
```

### 常用命令

```bash
# 运行程序
(gdb) run
(gdb) r

# 设置断点
(gdb) break main
(gdb) break file.c:100
(gdb) break function_name
(gdb) b main  # 简写

# 查看断点
(gdb) info breakpoints
(gdb) i b

# 删除断点
(gdb) delete 1
(gdb) d 1

# 继续执行
(gdb) continue
(gdb) c

# 单步执行（进入函数）
(gdb) step
(gdb) s

# 单步执行（不进入函数）
(gdb) next
(gdb) n

# 查看变量
(gdb) print variable_name
(gdb) p variable_name

# 查看变量类型
(gdb) ptype variable_name

# 查看局部变量
(gdb) info locals
(gdb) i locals

# 查看函数参数
(gdb) info args
(gdb) i args

# 查看堆栈
(gdb) backtrace
(gdb) bt

# 切换堆栈帧
(gdb) frame 1
(gdb) f 1

# 查看当前代码
(gdb) list
(gdb) l

# 退出GDB
(gdb) quit
(gdb) q
```

## LLDB基础

### LLDB简介

LLDB是LLVM项目的一部分，是macOS和iOS开发中的标准调试器，也支持Linux和Android。

### 安装LLDB

```bash
# macOS (Xcode自带)
# 或通过Homebrew
brew install llvm

# 检查版本
lldb --version
```

### 基本用法

```bash
# 启动LLDB
lldb program

# 附加到进程
lldb -p <PID>

# 加载core文件
lldb -c core program
```

### 常用命令

```bash
# 运行程序
(lldb) run
(lldb) r

# 设置断点
(lldb) breakpoint set --name main
(lldb) breakpoint set --file file.c --line 100
(lldb) b main  # 简写

# 查看断点
(lldb) breakpoint list
(lldb) br l

# 删除断点
(lldb) breakpoint delete 1
(lldb) br del 1

# 继续执行
(lldb) continue
(lldb) c

# 单步执行（进入函数）
(lldb) step
(lldb) s

# 单步执行（不进入函数）
(lldb) next
(lldb) n

# 查看变量
(lldb) print variable_name
(lldb) p variable_name

# 查看变量类型
(lldb) frame variable
(lldb) fr v

# 查看局部变量
(lldb) frame variable
(lldb) fr v

# 查看堆栈
(lldb) thread backtrace
(lldb) bt

# 切换线程
(lldb) thread select 1
(lldb) th s 1

# 查看当前代码
(lldb) source list
(lldb) l

# 退出LLDB
(lldb) quit
(lldb) q
```

## Android Native调试

### 1. 使用GDB调试Android Native代码

#### 准备工作

```bash
# 1. 确保应用是可调试的
# AndroidManifest.xml中设置 android:debuggable="true"

# 2. 推送gdbserver到设备
adb push $NDK/prebuilt/android-arm64/gdbserver/gdbserver /data/local/tmp/

# 3. 设置权限
adb shell chmod 755 /data/local/tmp/gdbserver
```

#### 启动调试会话

```bash
# 1. 启动应用并获取PID
adb shell ps | grep com.example.app

# 2. 在设备上启动gdbserver
adb shell /data/local/tmp/gdbserver :5039 --attach <PID>

# 3. 端口转发
adb forward tcp:5039 tcp:5039

# 4. 在主机上启动GDB
$NDK/prebuilt/linux-x86_64/bin/gdb

# 5. 在GDB中连接
(gdb) target remote :5039

# 6. 设置符号文件
(gdb) file /path/to/your/library.so
(gdb) set solib-search-path /path/to/symbols
```

### 2. 使用LLDB调试Android Native代码

#### 准备工作

```bash
# 1. 使用lldb-server（Android NDK r21+）
adb push $NDK/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/*/lib/linux/aarch64/lldb-server /data/local/tmp/

# 2. 设置权限
adb shell chmod 755 /data/local/tmp/lldb-server
```

#### 启动调试会话

```bash
# 1. 启动应用并获取PID
adb shell ps | grep com.example.app

# 2. 在设备上启动lldb-server
adb shell /data/local/tmp/lldb-server platform --server --listen "*:5039" --attach <PID>

# 3. 端口转发
adb forward tcp:5039 tcp:5039

# 4. 在主机上启动LLDB
lldb

# 5. 在LLDB中连接
(lldb) platform select remote-android
(lldb) platform connect connect://localhost:5039

# 6. 加载符号
(lldb) target create /path/to/your/library.so
```

### 3. Android Studio集成调试

Android Studio提供了图形化的Native调试界面：

1. **配置调试器**
   - Run -> Edit Configurations
   - 选择"Debugger"标签
   - 选择"Native"调试器类型

2. **设置断点**
   - 在Native代码中设置断点
   - 支持条件断点

3. **开始调试**
   - 点击Debug按钮
   - 应用会在断点处暂停

## 高级调试技巧

### 1. 条件断点

```gdb
# GDB
(gdb) break file.c:100 if variable == 5
(gdb) condition 1 variable > 10
```

```lldb
# LLDB
(lldb) breakpoint set --file file.c --line 100 --condition 'variable == 5'
(lldb) breakpoint modify 1 --condition 'variable > 10'
```

### 2. 观察点（Watchpoint）

```gdb
# GDB - 监控变量变化
(gdb) watch variable_name
(gdb) watch *0x12345678  # 监控内存地址
```

```lldb
# LLDB
(lldb) watchpoint set variable variable_name
(lldb) watchpoint set expression -- 0x12345678
```

### 3. 内存操作

```gdb
# GDB
(gdb) x/10x 0x12345678  # 查看内存（16进制，10个单位）
(gdb) x/10i $pc  # 查看指令
(gdb) x/s 0x12345678  # 查看字符串
```

```lldb
# LLDB
(lldb) memory read --format x --count 10 0x12345678
(lldb) memory read --format instruction --count 10 $pc
(lldb) memory read --format cstring 0x12345678
```

### 4. 寄存器操作

```gdb
# GDB
(gdb) info registers  # 查看所有寄存器
(gdb) print $rax  # 查看特定寄存器
(gdb) set $rax = 0x1234  # 设置寄存器值
```

```lldb
# LLDB
(lldb) register read  # 查看所有寄存器
(lldb) register read rax  # 查看特定寄存器
(lldb) register write rax 0x1234  # 设置寄存器值
```

### 5. 多线程调试

```gdb
# GDB
(gdb) info threads  # 查看所有线程
(gdb) thread 2  # 切换到线程2
(gdb) thread apply all bt  # 查看所有线程堆栈
```

```lldb
# LLDB
(lldb) thread list  # 查看所有线程
(lldb) thread select 2  # 切换到线程2
(lldb) thread backtrace all  # 查看所有线程堆栈
```

## 崩溃分析

### 1. 分析Core Dump

```bash
# 生成core文件
ulimit -c unlimited
./program

# 使用GDB分析
gdb program core
(gdb) bt  # 查看堆栈
(gdb) info registers  # 查看寄存器
```

### 2. 分析Tombstone（Android）

```bash
# 获取tombstone文件
adb pull /data/tombstones/tombstone_XX

# 使用addr2line解析地址
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-addr2line -e library.so <address>

# 或使用ndk-stack
$NDK/ndk-stack -sym /path/to/symbols -dump tombstone_XX
```

### 3. 使用GDB分析崩溃

```bash
# 1. 获取崩溃时的堆栈
adb shell gdb -p <PID>
(gdb) bt

# 2. 查看寄存器状态
(gdb) info registers

# 3. 查看内存内容
(gdb) x/10x $sp  # 查看栈内容
```

## 实战案例

### 案例1：调试JNI调用

```java
// Java层
public native void nativeMethod(int value);
```

```cpp
// Native层
JNIEXPORT void JNICALL
Java_com_example_MainActivity_nativeMethod(JNIEnv *env, jobject thiz, jint value) {
    // 设置断点
    int result = value * 2;
    // ...
}
```

**调试步骤**:
1. 在Native方法中设置断点
2. 从Java层调用Native方法
3. 在断点处检查参数和局部变量

### 案例2：调试内存泄漏

```cpp
void* allocateMemory(size_t size) {
    void* ptr = malloc(size);
    // 设置断点，检查分配的内存
    return ptr;
}
```

**调试步骤**:
1. 在分配函数中设置断点
2. 使用watchpoint监控指针变量
3. 检查内存是否被正确释放

### 案例3：调试多线程问题

```cpp
void* threadFunction(void* arg) {
    // 线程函数
    int* value = (int*)arg;
    *value = 42;
    return NULL;
}
```

**调试步骤**:
1. 在多个线程中设置断点
2. 使用`thread apply all bt`查看所有线程状态
3. 检查共享变量的访问

## 调试脚本

### GDB脚本示例

```gdb
# .gdbinit
# 自动加载符号
file /path/to/library.so
set solib-search-path /path/to/symbols

# 定义命令
define print_stack
    bt
    info registers
    x/20x $sp
end

# 自动执行
break main
run
```

### LLDB脚本示例

```python
# .lldbinit
# 定义命令
def print_stack(debugger, command, result, dict):
    """打印堆栈和寄存器"""
    debugger.HandleCommand("bt")
    debugger.HandleCommand("register read")
    debugger.HandleCommand("memory read --format x --count 20 $sp")

# 注册命令
lldb.debugger.HandleCommand("command script add -f print_stack print_stack")
```

## 常见问题

### 1. 符号文件找不到

```bash
# 确保使用带符号的库文件
# 设置符号搜索路径
(gdb) set solib-search-path /path/to/symbols
```

### 2. 无法附加到进程

```bash
# 检查应用是否可调试
adb shell getprop ro.debuggable

# 使用root权限
adb root
```

### 3. 断点不生效

```bash
# 确保代码已加载
(gdb) info sharedlibrary

# 检查断点位置是否正确
(gdb) info breakpoints
```

## 最佳实践

1. **使用符号文件**: 始终使用带调试符号的库文件
2. **记录调试过程**: 保存调试会话和关键信息
3. **自动化脚本**: 编写脚本简化重复操作
4. **版本匹配**: 确保调试器版本与目标匹配
5. **安全考虑**: 生产环境禁用调试功能

## 相关链接

- [[README]]
- [[ADB高级命令]]
- [[Systrace_Perfetto全解读]]