工欲善其事必先利其器嘛。
调试 Linux 驱动程序是开发过程中非常关键的环节,因为驱动程序直接与硬件交互,任何错误可能导致系统崩溃或硬件故障。调试 Linux 驱动程序可以通过多种方法和工具进行,以下是常见的调试方法和步骤:
### 1. 使用 `printk` 调试
`printk` 是 Linux 内核中最常用的调试方法之一。它类似于用户空间的 `printf`,可以将调试信息输出到内核日志中。
#### 1.1 使用 `printk`
- 在驱动代码中插入 `printk` 语句来打印调试信息:
```c
printk(KERN_INFO "MyDriver: Initialization started\n");
printk(KERN_DEBUG "MyDriver: Value of x = %d\n", x);
```- 内核日志级别:
- `KERN_EMERG`:紧急信息,系统崩溃前的信息。
- `KERN_ALERT`:需要立即处理的信息。
- `KERN_CRIT`:严重错误。
- `KERN_ERR`:错误信息。
- `KERN_WARNING`:警告信息。
- `KERN_NOTICE`:正常但重要的信息。
- `KERN_INFO`:信息级别。
- `KERN_DEBUG`:调试信息。- 查看内核日志:
- 使用 `dmesg` 命令查看内核日志:
```bash
dmesg | grep MyDriver
```
- 也可以通过 `journalctl -k` 查看系统日志。### 2. 使用 `gdb` 调试
`gdb` 是 GNU 调试器,可以用于调试用户空间应用和内核模块。
#### 2.1 内核模块调试
- 加载模块并附加 `gdb`:
- 使用 `kgdb`(Kernel GNU Debugger)或 `kgdboc`(Kernel GNU Debugger over Console)来调试内核模块。需要配置内核支持 `kgdb`。
- 启动内核调试:
1. 启动系统时,在内核启动参数中添加 `kgdboc=ttyS0,115200 kgdbwait`。
2. 在另一台计算机上使用 `gdb` 连接:
```bash
gdb vmlinux
(gdb) target remote /dev/ttyS0
```- 使用 QEMU 和 GDB 调试内核:
- 使用 QEMU 启动内核时,可以指定 GDB 调试端口,然后通过 GDB 连接调试:
```bash
qemu-system-x86_64 -kernel bzImage -append "kgdbwait kgdboc=ttyS0,115200" -serial stdio -s -S
```
然后在主机上启动 GDB 并连接到 QEMU:
```bash
gdb vmlinux
(gdb) target remote :1234
```### 3. 使用动态调试(Dynamic Debugging)
Linux 内核支持动态调试,可以在运行时控制 `printk` 语句的启用和禁用,而不需要重新编译内核或模块。
#### 3.1 启用动态调试
- 在内核启动参数中添加 `dynamic_debug.verbose=1`。
- 使用以下命令控制特定模块的调试信息:
```bash
echo 'module my_driver +p' > /sys/kernel/debug/dynamic_debug/control
```### 4. 使用 `ftrace` 进行函数跟踪
`ftrace` 是 Linux 内核中强大的跟踪工具,适用于函数调用跟踪和性能分析。
#### 4.1 使用 `ftrace` 跟踪函数
- 启用 `ftrace` 并跟踪特定函数:
```bash
echo function > /sys/kernel/debug/tracing/current_tracer
echo "my_driver_function" > /sys/kernel/debug/tracing/set_ftrace_filter
```- 通过查看 `/sys/kernel/debug/tracing/trace` 文件来获取跟踪信息:
```bash
cat /sys/kernel/debug/tracing/trace
```### 5. 使用 `kprobes` 和 `tracepoints`
#### 5.1 `kprobes`
`kprobes` 允许在运行时动态插入探针到内核代码中的特定地址,用于调试和监视。
- 插入 `kprobe`:
```c
static struct kprobe kp = {
.symbol_name = "target_function",
};int handler_pre(struct kprobe *p, struct pt_regs *regs) {
printk(KERN_INFO "kprobe pre_handler: function called\n");
return 0;
}kp.pre_handler = handler_pre;
register_kprobe(&kp);
```#### 5.2 `tracepoints`
`tracepoints` 是内核中预定义的跟踪点,驱动程序可以动态启用这些跟踪点以进行调试和性能分析。
- 使用 `tracepoints`:
- 在代码中插入 `tracepoint`:
```c
tracepoint(my_tp, my_event, arg1, arg2);
```- 在运行时启用跟踪:
```bash
echo 1 > /sys/kernel/debug/tracing/events/my_tp/my_event/enable
```### 6. 使用 `kgdb`(Kernel GNU Debugger)
`kgdb` 是用于调试运行中的 Linux 内核的调试工具。它可以帮助你在内核崩溃时查看寄存器、堆栈、内核变量等。
#### 6.1 设置 `kgdb`
- 配置内核支持 `kgdb`,并通过串口或网络接口调试内核。
- 启动调试会话并附加到内核。### 7. 使用虚拟机或模拟器
- **QEMU**:通过 QEMU 模拟内核环境,使用 GDB 进行调试。
- **Buildroot** 或 **Yocto**:创建自定义嵌入式 Linux 环境,并在虚拟机中调试。### 8. 查看 `sysfs` 和 `procfs`
通过 `/sys` 和 `/proc` 文件系统,可以访问和检查内核状态、驱动程序状态、设备配置等信息。
- 例如,检查模块的参数:
```bash
cat /sys/module/my_driver/parameters/param_name
```### 9. 内核崩溃时使用 `crash` 工具
当内核崩溃时,内存转储(`core dump`)可以用于调试。
- 使用 `crash` 工具分析内核崩溃:
```bash
crash vmlinux vmcore
```### 总结
调试 Linux 驱动程序是一个复杂的过程,需要使用多种工具和方法。`printk` 是最简单和最常用的方法,但对于复杂的驱动程序,使用 `gdb`、`ftrace`、`kprobes`、`kgdb` 等工具可以提供更深入的调试能力。此外,利用设备树和 `sysfs` 等接口可以方便地获取系统状态和配置,从而帮助快速定位问题。根据具体的调试需求和问题性质,选择合适的调试工具和方法可以有效提高开发效率和代码质量。
使用VScode调试驱动
要在 Qualcomm 嵌入式设备上使用 Visual Studio Code (VSCode) 调试 Linux 驱动程序,您需要进行一些配置,使得 VSCode 能够与目标设备进行通信并使用调试工具(如 GDB)调试驱动程序。以下是如何配置和使用 VSCode 调试部署在 Qualcomm 设备上的驱动程序的详细步骤:
### 1. 准备环境
#### 1.1 安装必要的工具
首先,在主机系统上安装以下工具:
- **VSCode**: 下载并安装最新版本的 Visual Studio Code。
- **GDB**: GNU 调试器,用于调试远程设备上的内核模块或驱动程序。
- **GDB Server**: 用于在目标设备上运行 GDB 服务器。
- **SSH**: 用于通过 SSH 远程访问设备(如果是通过网络连接设备)。#### 1.2 安装 VSCode 扩展
在 VSCode 中安装以下扩展:
- **C/C++**: Microsoft 提供的 C/C++ 语言扩展,支持调试和语法高亮。
- **Remote - SSH**: 用于通过 SSH 连接和调试远程设备。
- **Cortex-Debug**: 如果目标设备是基于 ARM 架构的 Qualcomm 设备,这个扩展可能会有所帮助。### 2. 在 Qualcomm 设备上设置调试环境
#### 2.1 确保设备上安装了 GDB Server
在 Qualcomm 设备上,您需要确保安装了 GDB Server。大多数嵌入式 Linux 系统都带有 GDB Server,您可以通过以下命令确认:
```bash
gdbserver --version
```如果没有安装 GDB Server,您可以使用包管理器(例如 `apt` 或 `opkg`)安装,或者手动编译并安装。
#### 2.2 加载内核模块
假设您要调试的驱动程序已经编译为内核模块(.ko 文件),您需要将其加载到设备中:
```bash
insmod my_driver.ko
```您可以在加载模块时通过 `dmesg` 查看内核日志,确保模块加载成功并获取调试信息。
### 3. 配置 VSCode 进行远程调试
#### 3.1 配置 SSH 连接
1. 打开 VSCode,点击左下角的远程图标,选择 `Remote-SSH: Connect to Host...`。
2. 输入设备的 SSH 地址,格式为 `user@hostname`,然后点击回车。
3. 在弹出的窗口中选择 `Linux` 系统类型。VSCode 将连接到远程设备,并安装必要的组件。#### 3.2 配置 `launch.json`
在 VSCode 中配置 `launch.json`,以便通过 GDB 远程调试您的驱动程序。以下是一个基本的配置示例:
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Qualcomm Driver",
"type": "cppdbg",
"request": "launch",
"program": "/path/to/vmlinux", // 目标设备上运行的内核映像路径
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb", // 本地主机的 gdb 路径
"setupCommands": [
{
"description": "Enable KGDB",
"text": "target remote localhost:1234"
},
{
"description": "Set Breakpoint",
"text": "break <function_name>" // 设置驱动程序中的断点
}
],
"logging": {
"engineLogging": true
},
"visualizerFile": "${workspaceFolder}/gdb_visualizer"
}
]
}
```- `program`: 指定设备上的内核映像路径(例如 `vmlinux`)。
- `miDebuggerPath`: 主机系统中 GDB 的路径。
- `setupCommands`: 包含连接到目标设备上的 GDB Server 的命令。#### 3.3 启动 GDB Server
在目标设备上,启动 GDB Server 并指定端口和要调试的进程(或内核):
```bash
gdbserver :1234 --attach <pid>
```如果您调试的是内核模块,可以通过以下方式启动 GDB Server,并使其等待 VSCode 的 GDB 客户端连接:
```bash
gdbserver :1234 /path/to/vmlinux
```### 4. 启动调试
1. 确保 GDB Server 在设备上运行。
2. 在 VSCode 中,选择刚刚配置的调试配置(例如 `Debug Qualcomm Driver`)。
3. 点击“开始调试”按钮,VSCode 将连接到远程设备上的 GDB Server 并开始调试。### 5. 调试过程
- **设置断点**: 在 VSCode 中,您可以通过单击行号设置断点。GDB 会在执行到该位置时暂停。
- **单步执行**: 使用 VSCode 的调试工具栏进行单步执行,或在控制台中输入 GDB 命令进行更精细的控制。
- **查看变量和内存**: 您可以在 VSCode 的“变量”窗口中查看当前上下文的变量值,或使用 GDB 命令查看内存状态。### 6. 总结
通过 VSCode 配置远程 SSH 连接,并结合 GDB 和 GDB Server,可以实现对部署在 Qualcomm 设备上的驱动程序的远程调试。VSCode 提供了良好的用户界面和调试体验,但配置步骤比较复杂,需要一定的熟悉度。您可以通过上述步骤设置调试环境,并根据实际需要调整 `launch.json` 文件,以适应不同的调试场景和设备配置。
Android的驱动
要在 Qualcomm Android 设备上使用 Visual Studio Code (VSCode) 调试 `.ko` 驱动程序,您可以通过结合 Android 调试桥 (ADB)、GDB 和 GDB Server 来实现远程调试。以下是详细步骤:
### 1. 准备环境
#### 1.1 安装必要工具
在主机系统上安装以下工具:
- **VSCode**: 安装 Visual Studio Code。
- **Android SDK 和 NDK**: Android SDK 包含 ADB,NDK 包含用于调试的 GDB。
- **ADB**: Android 调试桥,用于与 Android 设备通信。
- **GDB 和 GDB Server**: 用于远程调试驱动程序。#### 1.2 安装 VSCode 扩展
在 VSCode 中安装以下扩展:
- **C/C++**: 支持 C/C++ 语言的调试和语法高亮。
- **Android**: 允许 VSCode 与 Android 设备进行交互。
- **Remote - SSH** (可选): 如果需要通过 SSH 远程连接到 Android 设备,可以使用此扩展。### 2. 设置 Android 设备
#### 2.1 启用开发者选项和 USB 调试
1. 在 Android 设备上,进入“设置” -> “关于手机”。
2. 连续点击“版本号”七次,启用开发者选项。
3. 在“开发者选项”中,启用“USB 调试”。#### 2.2 使用 ADB 连接设备
通过 USB 连接 Android 设备,并使用 ADB 确认连接:
```bash
adb devices
```确保设备在列表中显示。
### 3. 编译并加载 `.ko` 驱动程序
#### 3.1 编译驱动程序
首先,确保已经在主机上编译好驱动程序的 `.ko` 文件,并将其推送到 Android 设备:
```bash
adb push my_driver.ko /data/local/tmp/
```#### 3.2 加载驱动程序
使用 ADB shell 进入设备,然后加载驱动程序:
```bash
adb shell
insmod /data/local/tmp/my_driver.ko
```使用 `dmesg` 检查驱动程序是否正确加载。
### 4. 设置 GDB 和 GDB Server
#### 4.1 在 Android 设备上启动 GDB Server
启动 GDB Server 并附加到内核:
```bash
adb shell
gdbserver :5039 --attach $(pgrep -n my_driver) # 使用你的驱动进程名
```#### 4.2 配置 ADB 端口转发
将设备上的 GDB 端口转发到本地主机上:
```bash
adb forward tcp:5039 tcp:5039
```### 5. 配置 VSCode 进行远程调试
#### 5.1 配置 `launch.json`
在 VSCode 工作区中创建或修改 `.vscode/launch.json` 文件,配置 GDB 远程调试:
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Android Kernel Module",
"type": "cppdbg",
"request": "launch",
"program": "/path/to/vmlinux", // 内核映像的路径
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerServerAddress": "localhost:5039", // 本地转发的端口
"miDebuggerPath": "/path/to/gdb", // GDB 的路径,一般位于 NDK 中
"setupCommands": [
{
"description": "Connect to target",
"text": "target remote localhost:5039"
},
{
"description": "Set Breakpoint",
"text": "break <your_module_function>" // 需要调试的函数
}
],
"logging": {
"engineLogging": true
},
"visualizerFile": "${workspaceFolder}/gdb_visualizer"
}
]
}
```关键字段:
- `program`: 指定设备上的内核映像(如 `vmlinux`)。
- `miDebuggerServerAddress`: ADB 转发的端口。
- `miDebuggerPath`: 主机上 GDB 的路径,通常在 Android NDK 中。### 6. 启动调试
1. 确保 GDB Server 正在设备上运行,并且 ADB 端口已正确转发。
2. 在 VSCode 中,选择 `Debug Android Kernel Module` 配置。
3. 点击“开始调试”按钮,VSCode 将连接到设备上的 GDB Server,并开始调试。### 7. 调试过程
- **设置断点**: 在 VSCode 中,点击行号区域设置断点。
- **单步执行**: 使用调试工具栏中的按钮进行单步调试。
- **查看变量和内存**: 在“变量”窗口中查看变量值,或使用 GDB 命令检查内存内容。### 8. 总结
通过 VSCode 配置远程调试,可以有效调试部署在 Qualcomm Android 设备上的 `.ko` 驱动程序。这个流程结合了 ADB、GDB 和 GDB Server 的使用,尽管设置较为复杂,但提供了强大的调试能力,适合深度分析和解决驱动程序的问题。