🎯 学习目标
完成本章后,你将能够:
- ✅ 使用 GDB 调试 IGT 测试
- ✅ 分析测试失败的原因
- ✅ 使用日志和调试输出
- ✅ 查看内核日志和 GPU 状态
- ✅ 排查常见问题
🔍 调试工具概览
可用的调试工具
| 工具 | 用途 | 优先级 |
|---|---|---|
| GDB | 源码级调试 | ⭐⭐⭐⭐⭐ |
| dmesg | 内核日志 | ⭐⭐⭐⭐⭐ |
| strace | 系统调用跟踪 | ⭐⭐⭐⭐ |
| valgrind | 内存错误检测 | ⭐⭐⭐ |
| perf | 性能分析 | ⭐⭐⭐ |
| IGT 日志 | 测试输出 | ⭐⭐⭐⭐⭐ |
📊 IGT 日志系统
日志级别
IGT 提供多个日志级别:
c
igt_debug("详细调试信息\n"); // 最详细
igt_info("一般信息\n"); // 正常输出
igt_warn("警告信息\n"); // 可能有问题
igt_critical("严重错误\n"); // 必须关注
设置日志级别
方法 1:环境变量
bash
# 显示所有调试信息
IGT_LOG_LEVEL=debug sudo ./build/tests/my_test
# 只显示警告和错误
IGT_LOG_LEVEL=warn sudo ./build/tests/my_test
# 可选值:debug, info, warn, critical, none
方法 2:代码中设置
c
igt_main
{
igt_fixture {
// 设置为调试级别
igt_log_level_set(IGT_LOG_DEBUG);
}
igt_subtest("my-test") {
igt_debug("这是调试信息\n");
igt_info("这是普通信息\n");
}
}
实用的日志宏
c
// 打印变量
igt_info("fd = %d\n", fd);
igt_info("buffer size = %zu bytes\n", size);
igt_info("address = %p\n", ptr);
// 条件日志
if (igt_log_level >= IGT_LOG_DEBUG) {
dump_debug_info();
}
// 断言失败时的信息
igt_assert_f(condition, "Expected %d, got %d\n", expected, actual);
🐛 使用 GDB 调试
基本 GDB 使用
启动调试:
bash
# 调试整个测试
sudo gdb --args ./build/tests/my_test
# 调试特定子测试
sudo gdb --args ./build/tests/my_test --run-subtest my-subtest
常用 GDB 命令:
gdb
# 运行程序
(gdb) run
# 在函数设置断点
(gdb) break main
(gdb) break igt_subtest_f
# 在文件的特定行设置断点
(gdb) break my_test.c:42
# 继续执行
(gdb) continue
# 单步执行(进入函数)
(gdb) step
# 单步执行(跳过函数)
(gdb) next
# 查看变量
(gdb) print fd
(gdb) print *version
# 查看调用栈
(gdb) backtrace
(gdb) bt
# 查看源码
(gdb) list
# 退出
(gdb) quit
调试示例
场景:测试在某个子测试中崩溃
bash
# 1. 启动 GDB
sudo gdb --args ./build/tests/my_test --run-subtest crash-test
# 2. 设置断点
(gdb) break my_test.c:100
# 3. 运行
(gdb) run
# 4. 程序停在断点
(gdb) print fd
$1 = 3
# 5. 查看调用栈
(gdb) backtrace
#0 my_function () at my_test.c:100
#1 0x... in __igt_subtest_main () at igt_core.c:...
#2 0x... in main () at my_test.c:200
# 6. 继续执行到崩溃
(gdb) continue
# 7. 查看崩溃信息
(gdb) backtrace
(gdb) info registers
条件断点
gdb
# 只在 i == 10 时断点
(gdb) break my_test.c:50 if i == 10
# 只在指针非空时断点
(gdb) break my_test.c:60 if ptr != NULL
观察点(Watchpoint)
gdb
# 监视变量变化
(gdb) watch fd
(gdb) watch *buffer
# 当变量改变时停止
Hardware watchpoint 1: fd
📋 内核日志分析
查看 dmesg
实时查看:
bash
# 持续监控内核日志
sudo dmesg -w
# 带时间戳
sudo dmesg -T -w
# 只看错误
sudo dmesg -l err,crit,alert,emerg
过滤 DRM 相关日志:
bash
# 查看 DRM 日志
sudo dmesg | grep drm
# 查看 i915 驱动日志
sudo dmesg | grep i915
# 查看 amdgpu 驱动日志
sudo dmesg | grep amdgpu
# 实时过滤
sudo dmesg -w | grep -E "drm|i915|amdgpu"
启用内核调试
临时启用:
bash
# 启用 DRM 核心调试
echo 0x1e | sudo tee /sys/module/drm/parameters/debug
# 调试级别说明:
# 0x01 - CORE
# 0x02 - DRIVER
# 0x04 - KMS
# 0x08 - PRIME
# 0x10 - ATOMIC
# 0x20 - VBL
# 0x1e - 全部(除了 VBL)
启动时启用:
bash
# 编辑 GRUB 配置
sudo vim /etc/default/grub
# 添加内核参数
GRUB_CMDLINE_LINUX="drm.debug=0x1e"
# 更新 GRUB
sudo update-grub # Debian/Ubuntu
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # Fedora
# 重启生效
查看 GPU 错误状态
Intel GPU:
bash
# GPU 错误状态
sudo cat /sys/class/drm/card0/error
# 解码错误
intel_error_decode < /sys/class/drm/card0/error > decoded_error.txt
# i915 调试信息
sudo cat /sys/kernel/debug/dri/0/i915_error_state
sudo cat /sys/kernel/debug/dri/0/i915_capabilities
AMD GPU:
bash
# GPU 信息
sudo cat /sys/kernel/debug/dri/0/amdgpu_pm_info
sudo cat /sys/kernel/debug/dri/0/amdgpu_gfx_info
# VRAM 使用
sudo cat /sys/class/drm/card0/device/mem_info_vram_used
🔬 系统调用跟踪
使用 strace
基本用法:
bash
# 跟踪所有系统调用
sudo strace ./build/tests/my_test
# 只跟踪 ioctl 调用
sudo strace -e ioctl ./build/tests/my_test
# 跟踪文件操作
sudo strace -e open,close,read,write ./build/tests/my_test
# 保存到文件
sudo strace -o trace.log ./build/tests/my_test
分析 DRM ioctl:
bash
# 跟踪 DRM ioctl
sudo strace -e ioctl ./build/tests/my_test 2>&1 | grep DRM
# 示例输出:
# ioctl(3, DRM_IOCTL_VERSION, ...)
# ioctl(3, DRM_IOCTL_GET_CAP, ...)
# ioctl(3, DRM_IOCTL_MODE_GETRESOURCES, ...)
统计系统调用:
bash
# 统计调用次数和时间
sudo strace -c ./build/tests/my_test
# 输出示例:
# % time seconds usecs/call calls errors syscall
# ------ ----------- ----------- --------- --------- ----------------
# 45.67 0.000823 27 30 ioctl
# 23.45 0.000422 14 30 read
# ...
🧪 内存调试
使用 Valgrind
检测内存泄漏:
bash
# 基本内存检查
valgrind --leak-check=full ./build/tests/my_test
# 详细输出
valgrind --leak-check=full --show-leak-kinds=all \
--track-origins=yes ./build/tests/my_test
# 保存到文件
valgrind --leak-check=full --log-file=valgrind.log \
./build/tests/my_test
示例输出:
==12345== HEAP SUMMARY:
==12345== in use at exit: 4,096 bytes in 1 blocks
==12345== total heap usage: 100 allocs, 99 frees, 40,960 bytes allocated
==12345==
==12345== LEAK SUMMARY:
==12345== definitely lost: 4,096 bytes in 1 blocks
IGT 内存检查
IGT 有内置的内存泄漏检测:
c
igt_main
{
igt_fixture {
// IGT 会自动跟踪 gem_create 等
fd = drm_open_driver(DRIVER_INTEL);
}
igt_subtest("leak-test") {
uint32_t handle;
handle = gem_create(fd, 4096);
// 忘记 gem_close(fd, handle); // 会被 IGT 检测到
}
// IGT 会在这里检查泄漏
}
📈 性能分析
使用 perf
记录性能数据:
bash
# 记录测试执行
sudo perf record ./build/tests/my_test
# 查看报告
sudo perf report
# 记录特定事件
sudo perf record -e cycles,instructions ./build/tests/my_test
分析热点函数:
bash
# 生成火焰图需要的数据
sudo perf record -F 99 -g ./build/tests/my_test
sudo perf script > out.perf
# 查看最耗时的函数
sudo perf report --sort=dso,symbol
IGT 性能测量
c
#include "igt_stats.h"
igt_subtest("performance") {
igt_stats_t stats;
struct timespec start, end;
igt_stats_init(&stats);
for (int i = 0; i < 100; i++) {
clock_gettime(CLOCK_MONOTONIC, &start);
// 执行操作
do_operation();
clock_gettime(CLOCK_MONOTONIC, &end);
double elapsed = igt_nsec_elapsed(&start, &end);
igt_stats_push(&stats, elapsed);
}
igt_info("平均: %.2f ns\n", igt_stats_get_mean(&stats));
igt_info("中位数: %.2f ns\n", igt_stats_get_median(&stats));
igt_info("标准差: %.2f ns\n", igt_stats_get_std_deviation(&stats));
igt_stats_fini(&stats);
}
🔧 常见问题排查
问题 1:测试卡住不动
症状:测试运行后无响应。
调试步骤:
bash
# 1. 在另一个终端查看进程
ps aux | grep my_test
# 2. 查看进程调用栈
sudo cat /proc/$(pidof my_test)/stack
# 3. 使用 gdb 附加
sudo gdb -p $(pidof my_test)
(gdb) thread apply all bt
(gdb) continue
# 4. 检查是否等待锁
sudo cat /proc/$(pidof my_test)/status | grep State
常见原因:
- 等待 GPU 操作完成
- 死锁
- 等待用户输入
问题 2:断言失败
症状 :Test assertion failure
调试示例:
bash
# 运行测试并保存输出
sudo ./build/tests/my_test 2>&1 | tee test.log
# 查看断言失败的详细信息
grep "assertion" test.log
# 示例输出:
# (my_test:12345) CRITICAL: Test assertion failure function main
# (my_test:12345) CRITICAL: Failed assertion: ret == 0
# (my_test:12345) CRITICAL: Last errno: 22, Invalid argument
使用 GDB 调试:
bash
sudo gdb --args ./build/tests/my_test --run-subtest failing-test
(gdb) break igt_assert_failed
(gdb) run
# 当断点触发时
(gdb) backtrace
(gdb) print ret
(gdb) print errno
问题 3:间歇性失败
症状:测试有时通过,有时失败。
调试技巧:
bash
# 1. 循环运行测试
for i in {1..100}; do
echo "=== 运行 $i ==="
sudo ./build/tests/my_test --run-subtest flaky-test || break
done
# 2. 添加详细日志
IGT_LOG_LEVEL=debug sudo ./build/tests/my_test
# 3. 检查竞态条件
# 在代码中添加睡眠来改变时序
igt_subtest("race-test") {
usleep(100000); // 100ms
// 测试代码
}
问题 4:权限被拒绝
症状 :Permission denied 或 EACCES
检查清单:
bash
# 1. 检查设备权限
ls -l /dev/dri/card*
# 2. 检查用户组
groups
id
# 3. 临时添加到 video 组
sudo usermod -aG video $USER
# 4. 或使用 sudo
sudo ./build/tests/my_test
# 5. 检查 SELinux(Fedora/RHEL)
getenforce
sudo setenforce 0 # 临时禁用测试
问题 5:找不到设备
症状 :No DRM device found 或 ENOENT
检查步骤:
bash
# 1. 列出 DRM 设备
ls -la /dev/dri/
# 2. 检查驱动加载
lsmod | grep -E "drm|i915|amdgpu|nouveau"
# 3. 加载驱动
sudo modprobe i915
sudo modprobe amdgpu
# 4. 查看内核日志
sudo dmesg | tail -50
# 5. 使用 lspci 查看 GPU
lspci | grep -i vga
lspci -k -s 00:02.0 # 替换为你的 GPU PCI 地址
🛠️ 调试脚本工具
自动调试脚本
bash
#!/bin/bash
# debug_igt.sh - IGT 测试调试助手
TEST_NAME=$1
SUBTEST=$2
if [ -z "$TEST_NAME" ]; then
echo "用法: $0 <test_name> [subtest]"
exit 1
fi
echo "=== IGT 调试助手 ==="
echo "测试: $TEST_NAME"
echo "子测试: ${SUBTEST:-all}"
echo ""
# 1. 检查测试是否存在
if [ ! -f "./build/tests/$TEST_NAME" ]; then
echo "❌ 测试不存在: $TEST_NAME"
exit 1
fi
# 2. 检查设备
echo "=== 检查 DRM 设备 ==="
ls -la /dev/dri/
echo ""
# 3. 检查驱动
echo "=== 检查驱动 ==="
lsmod | grep -E "drm|i915|amdgpu" || echo "未找到 DRM 驱动"
echo ""
# 4. 清空内核日志
sudo dmesg -C
# 5. 运行测试
echo "=== 运行测试 ==="
if [ -n "$SUBTEST" ]; then
IGT_LOG_LEVEL=debug sudo ./build/tests/$TEST_NAME \
--run-subtest $SUBTEST 2>&1 | tee test.log
else
IGT_LOG_LEVEL=debug sudo ./build/tests/$TEST_NAME 2>&1 | tee test.log
fi
TEST_RESULT=$?
# 6. 保存内核日志
echo ""
echo "=== 内核日志 ==="
sudo dmesg > dmesg.log
tail -50 dmesg.log
# 7. 显示结果
echo ""
echo "=== 测试结果 ==="
if [ $TEST_RESULT -eq 0 ]; then
echo "✅ 测试通过"
else
echo "❌ 测试失败 (退出码: $TEST_RESULT)"
echo ""
echo "日志文件:"
echo " - test.log (测试输出)"
echo " - dmesg.log (内核日志)"
fi
使用方法:
bash
chmod +x debug_igt.sh
./debug_igt.sh my_test
./debug_igt.sh my_test my-subtest
日志分析脚本
bash
#!/bin/bash
# analyze_failure.sh - 分析测试失败
LOG_FILE=$1
if [ -z "$LOG_FILE" ]; then
echo "用法: $0 <log_file>"
exit 1
fi
echo "=== 分析测试日志: $LOG_FILE ==="
echo ""
# 1. 统计子测试结果
echo "=== 子测试统计 ==="
grep "Subtest.*:" $LOG_FILE | awk '{print $2}' | sort | uniq -c
echo ""
# 2. 查找失败的子测试
echo "=== 失败的子测试 ==="
grep "Subtest.*FAIL" $LOG_FILE
echo ""
# 3. 查找断言失败
echo "=== 断言失败 ==="
grep "Test assertion failure" $LOG_FILE -A 3
echo ""
# 4. 查找警告
echo "=== 警告信息 ==="
grep "WARN" $LOG_FILE | head -10
echo ""
# 5. 查找错误
echo "=== 错误信息 ==="
grep -E "ERROR|CRITICAL" $LOG_FILE | head -10
📚 调试最佳实践
1. 逐步缩小范围
bash
# 第一步:确认问题
./build/tests/my_test
# 第二步:找到失败的子测试
./build/tests/my_test --run-subtest problem-test
# 第三步:添加调试输出
IGT_LOG_LEVEL=debug ./build/tests/my_test --run-subtest problem-test
# 第四步:使用 GDB
gdb --args ./build/tests/my_test --run-subtest problem-test
2. 添加调试代码
c
igt_subtest("debug-test") {
igt_info("开始测试\n");
int fd = drm_open_driver(DRIVER_ANY);
igt_info("fd = %d\n", fd);
drmVersionPtr ver = drmGetVersion(fd);
igt_info("版本: %s %d.%d.%d\n",
ver->name,
ver->version_major,
ver->version_minor,
ver->version_patchlevel);
// 测试逻辑...
igt_info("测试结束\n");
}
3. 保存现场
bash
# 保存所有调试信息
mkdir -p debug_info
sudo ./build/tests/my_test 2>&1 | tee debug_info/test.log
sudo dmesg > debug_info/dmesg.log
sudo cat /sys/kernel/debug/dri/0/i915_error_state > debug_info/gpu_error.log
lspci -vv > debug_info/lspci.log
4. 比较正常和异常情况
bash
# 运行正常的测试并保存日志
./build/tests/working_test 2>&1 > good.log
# 运行失败的测试并保存日志
./build/tests/failing_test 2>&1 > bad.log
# 比较差异
diff -u good.log bad.log
📚 下一步
现在你已经掌握了调试技巧,接下来:
👉 继续阅读 :13-高级主题 - 学习高级测试技术
或者:
- 实践调试:故意制造错误并调试
- 阅读内核文档:了解 DRM 调试接口
- 研究失败的测试:分析实际的测试失败
📎 参考资料
GDB 资源
- GDB 手册:https://sourceware.org/gdb/documentation/
- GDB 快速参考:
man gdb
内核调试
- DRM 调试:https://www.kernel.org/doc/html/latest/gpu/drm-internals.html
- 内核日志:
man dmesg
工具文档
- strace:
man strace - valgrind:https://valgrind.org/docs/
- perf:https://perf.wiki.kernel.org/
IGT 调试
lib/igt_core.h- 日志系统lib/igt_debugfs.h- debugfs 访问docs/- 官方文档