DOCA DPA GDB 服务器工具
本文档介绍了 DPA GDB 服务器工具。
DPA GDB 服务器工具目前处于 Beta 测试阶段。
DPA GDB 服务器工具 (dpa-gdbserver
) 能够调试 FlexIO DEV 程序。
用于调试的 DEV 程序使用 FlexIO 进程所有者提供的令牌(8 字节值)进行选择。
任何熟悉 RISC-V 架构的 GDB 均可用于调试。有关如何使用 GDB 的信息,请参阅此页面。
术语表
术语 | 描述 |
PUD | 被调试进程。用于调试的 DEV 端进程。 |
EU | 执行单元(类似于硬件 CPU 核心) |
DPA | 数据路径加速器 |
RPC | 远程进程通信。FlexIO 中用于立即运行 DEV 端代码的机制。运行时限制为 6 秒。 |
HOST | 管理 dev 端代码(即 DEV)的 x86 或 aarch64 Linux 操作系统 |
DEV | RISC-V 代码,由 HOST 加载到 DPA 的设备中。由不同类型的中断触发运行。DEV 端直接连接到 ConnectX 适配器卡。 |
GDB | GNU 项目调试器。允许用户在程序执行时对其进行监控。 |
GDBSERVER | 用于远程调试程序的工具 |
RTOS | 在 RISC-V 核心上运行的实时操作系统。管理中断处理和对 DEV 用户进程例程的调用。 |
RSP | 远程串行协议。用于 GDB 和 GDBSERVER 之间的交互。 |
已知限制
DPA GDB 技术不会捕获致命错误。因此,如果发生致命错误,则应使用 core dump(由
flexio_coredump_create()
创建)。DPA GDB 技术不支持 Outbox 访问。GDB 用户无法写入 Doorbell 或 Window 配置区域。
DPA GDB 技术不支持 Window 访问。对 Window 内存的读/写操作无法正常工作。
令牌
被调试进程 (PUD) 可以公开调试令牌。每个使用此令牌的外部进程都可以完全访问具有给定令牌的进程。为了不 постоянно 显示它(例如,出于安全原因),用户可以临时修改其主机应用程序。请参阅 flexio_process_udbg_token_get()
。
应用程序启动时连接
如果需要调试的代码在启动后立即开始运行,则用户应修改主机应用程序以在启动时停止,以便用户有时间运行 dpa-gdbserver
。一种可能的方法是在进程创建后立即放置函数 getchar()
。
虚拟线程概念
DPA 调试需要考虑的是,PUD 并非始终都有正在运行的线程(例如,进程的线程可能存在,但正在等待传入的数据包)。在常规 Linux 应用程序中,这种情况是不可能的,GDB 也不支持这种情况。
因此,当没有线程运行时,dpa-gdbserver
会报告一个虚拟线程
gdb
(gdb) info thread
Id Target Id Frame
* 1
Thread 1.805378433
(Dummy Flexio thread) 0x0800000000000000
in
?? ()
(gdb)
在这种情况下,用户可以检查内存、创建断点并给出 continue
命令。
对于虚拟线程,无法执行 step
、next
和 stepi
等命令。
看门狗问题
RTOS 具有看门狗定时器,它将 DEV 代码中断进程限制为 120 秒。当用户使用 GDB 连接到 DEV 时,此定时器会停止。因此,用户在调试时将没有时间限制。
默认情况下,dpa-gdbserver
使用 TCP 端口 1981 并在 EU 29 上运行。如果这与其他应用程序冲突(或者如果其他 dpa-gdbserver
实例正在运行),用户应按如下方式更改默认设置
Bash
$> dpa-gdbserver mlx5_0 -T <token> -s <port> -E <eu_id>
调试准备
如果需要,修改您的 FlexIO 应用程序。确保 HOST 代码打印 udbg_token
并在需要时等待 GDB 连接
C 代码。主机端。diff
+ uint64_t udbg_token;
flexio_process_create(..., &flexio_process);
+ udbg_token = flexio_process_udbg_token_get(flexio_process);
+ if
(udbg_token)
+ printf
("Process created. Use token >>> %#lx <<< for debug\n"
, udbg_token);
+ printf
("Stop point for waiting of GDB connection. Press Enter to continue..."
); /* Usually you don't need this stop point */
+ fflush
(stdout);
+ getchar
();
从 FlexIO 应用程序中提取 DPA 应用程序。例如
Bash
$> dpacc-extract cc-host/app/host/flexio_app_name -o flexio_app_name.rv5
开始调试
运行您的 FlexIO 应用程序。它应公开调试令牌
Bash
$> flexio_app_name mlx5_0 Process created. Use token >>> 0xd6278388ce4e682c <<<
for
debug使用收到的调试令牌运行
dpa-gdbserver
Bash
$> dpa-gdbserver mlx5_0 -T 0xd6278388ce4e682c Registered on device mlx5_0 Listening
for
GDB connection on port 1981运行任何支持 RISC-V 的 GDB。例如,
gdb-multiarch
Bash
$> gdb-multiarch -q flexio_app_name.rv5 Reading symbols from flexio_app_name.rv5... (gdb)
如果需要,使用正确的 TCP 端口和主机名连接到 gdbserver
gdb
(gdb) target remote :
1981
Remote debugging using :1981
0x0800000000000000
in
?? ()
DPA 特定调试技术
从虚拟线程过渡到真实线程的简单示例
在虚拟线程和真实线程之间切换不是 GDB 下调试的标准做法。在理想情况下,用户应确切知道其所有例程的入口点,并可以为所有例程设置断点。然后,用户可以运行 continue
命令
gdb
(gdb) target remote :1981
Remote debugging using :1981
0x0800000000000000
in
?? ()
(gdb) info threads
Id Target Id Frame
* 1
Thread 1.805378433
(Dummy Flexio thread) 0x0800000000000000
in
?? ()
(gdb) b foo
Breakpoint 1
at 0x400000b2
: file ../tests/path/hello.c, line 58
.
(gdb) b bar
Breakpoint 2
at 0x40000518
: file ../tests/path/hallo.c, line 113
.
(gdb) continue
Continuing.
为您的 DEV 程序启动中断(取决于您的任务),GDB 应捕获断点,现在 PUD 的真实线程应代替虚拟线程出现
gdb
(gdb) continue
Continuing.
(gdb) [New Thread 1.2
]
[New Thread 1.130
]
[New Thread 1.258
]
[New Thread 1.386
]
[Switching to Thread 1.2
]
Thread 2
hit Breakpoint 1
, foo(thread_arg=9008
)
at ../tests/path/hello.c:58
58
struct host_data *hdata = NULL;
(gdb) info threads
Id Target Id Frame
* 2
Thread 1.2
(Process 0
thread 0x1
GVMI 0
) foo (arg=9008
) at ../tests/path/hello.c:58
3
Thread 1.130
(Process 0
thread 0x81
GVMI 0
) foo (arg=9264
) at ../tests/path/hello.c:58
4
Thread 1.258
(Process 0
thread 0x101
GVMI 0
) foo (arg=9648
) at ../tests/path/hello.c:58
5
Thread 1.386
(Process 0
thread 0x181
GVMI 0
) foo (arg=9904
) at ../tests/path/hello.c:58
(gdb)
从此时起,您可以像往常一样检查内存和跟踪代码。
从虚拟线程过渡到真实线程的复杂示例
在更复杂的情况下,中断发生在 GDB 连接之后。在这种情况下,真实线程应开始运行,但由于 PUD 处于 HALT 状态而无法运行。用户可以键入命令 info threads
,看到新线程代替旧的虚拟线程,然后手动切换到新线程
gdb
(gdb) target remote :1981
Remote debugging using :1981
0x0800000000000000
in
?? ()
(gdb) info threads
Id Target Id Frame
* 1
Thread 1.805378433
(Dummy Flexio thread) 0x0800000000000000
in
?? ()
(gdb) info threads
[New Thread 1.32769
]
Id Target Id Frame
2
Thread 1.32769
(Process 0
thread 0x8000
GVMI 0
) bar (arg=0xc0
, len=0
)
at /path/lib/src/stub.c:167
The current thread <Thread ID 1
> has terminated. See `help thread'.
(gdb) thread 2
[Switching to thread 2
(Thread 1.32769
)]
#0
bar (arg=0xc0
, len=0
)
at /path/lib/src/stub.c:167
167
{
(gdb) bt
#0
bar (arg=0xc0
, len=0
)
at /path/lib/src/stub.c:167
#1
0x000000004000017a
in
foo (thread_arg=3221
)
at ../path/dev/hello.c:182
#2
0x0000000000000000
in
?? ()
Backtrace
stopped: frame did not save the PC
(gdb)
第 4 行和第 7 行中的相同命令 info threads
给出不同的结果。发生这种情况是因为中断发生在实例之间,并且真实代码开始运行。
用户必须手动切换到新线程(请参阅第 14 行)。之后,他们可以像往常一样跟踪/调试流程(即,使用命令 step
、next
、stepi
)。
在不完成 PUD 的情况下完成真实线程
每个中断处理程序在某个时候都会完成其路径,并将 CPU 资源返回给 RTOS。最常见的方法是调用函数 flexio_dev_thread_reschedule()
。对此函数执行 next
命令将具有与 continue
命令相同的效果
gdb
205
__dpa_thread_fence(__DPA_MEMORY, __DPA_W, __DPA_W);
(gdb) next
206
flexio_dev_cq_arm(dtctx, app_ctx.rq_cq_ctx.cq_idx, app_ctx.rq_cq_ctx.cq_number);
(gdb) next
208
if
((dev_errno = flexio_dev_get_and_rst_errno(dtctx))) {
(gdb) next
213
print_sim_str("Nothing to do. Wait for next duar\n"
, 0
);
(gdb) next
214
flexio_dev_thread_reschedule();
(gdb) next
GDB 将一直等待,直到用户键入 ^C
或在下一次中断发生后到达断点。
DPA GDB 服务器工具已通过 gdb-multiarch
(版本 9.2)和 RISC-V 工具链中的 GDB 版本 12.1 验证。
GDB 服务器应支持 GDB RSP(远程串行协议)中针对 GDB 桩描述的所有命令。但仅支持最常见的 GDB 命令。
如果 dpa-gdbserver 发生错误,请提供以下数据
使用的 GDB(名称和版本)
重现问题的命令序列
DPA GDB 服务器工具控制台输出
DPA GDB 服务器工具日志目录内容(有关详细信息,请参阅下一部分)
可选 - 以详细模式运行
dpa-gdbserver
时打印的输出数据
工具日志目录
对于每次运行,都会创建一个临时目录,模板为 /tmp/flexio_gdbs.XXXXXX
。
要找到最新的目录,请运行以下命令
Bash
$> ls
-ldtr /tmp/flexio_gdbs.* | tail
gdbserver 的详细程度
默认情况下,dpa-gdbserver
不会将任何日志信息打印到屏幕。向命令行添加 - v
选项会增加详细程度,从而将其他信息打印到 dpa-gdbserver
终端显示屏。详细程度根据命令行开关中“v”的数量递增(即 -vv
、-vvv
等)。
一个 -v
显示 RSP 交换。这是一个文本协议,因此用户可以读取和理解来自 GDB 的请求以及来自 GDB 服务器的响应
gdbserver.log -v
<<<<< "qTStatus"
>>>>> ""
<<<<< "?"
>>>>> "S05"
<<<<< "qfThreadInfo"
>>>>> "mp01.30011981"
<<<<< "qsThreadInfo"
>>>>> "l"
<<<<< "qAttached:1"
>>>>> "1"
<<<<< "Hc-1"
>>>>> "OK"
<<<<< "qC"
>>>>> "QCp01.30011981"
在示例中,<<<<<
和 >>>>>
分别用于指示从 GDB 接收和传输到 GDB 的数据。
当以更高的详细程度运行时(例如,使用选项 -vv
或更高版本运行 dpa-gdbserver
),将显示与 RTOS 模块的交换
gdbserver.log -vv
<<<<< "qfThreadInfo"
/ 2
/dgdbs_handler - cmd 0x5
/ 2
/dgdbs_handler - retval 0x4
>>>>> "mp01.30011981"
<<<<< "qsThreadInfo"
/ 2
/dgdbs_handler - cmd 0x5
/ 2
/dgdbs_handler - retval 0x5
>>>>> "l"
<<<<< "m800000000000000,4"
/ 2
/dgdbs_handler - cmd 0xc
/ 2
/dgdbs_handler - retval 0x9
>>>>> "E0a"
<<<<< "m7fffffffffffffc,4"
/ 2
/dgdbs_handler - cmd 0xc
/ 2
/dgdbs_handler - retval 0x9
>>>>> "E0a"
<<<<< "qSymbol::"
>>>>> "OK"
以 / #/
开头的行提供从 DEV 端打印的内部 RTOS 线程数。
本节提供有关命令和方法的有用信息,这些命令和方法可以在用户执行 DPA 调试时提供帮助。这与 dpa-gdbserver
本身无关。但这与远程调试和 FlexIO 源代码有关。
命令 "directory"
GDB 可以在与完成编译的主机不同的主机上运行。例如,用户可能已在 host1
上编译并运行其应用程序,并在 host2
上运行其 GDB 实例。在这种情况下,用户将看到错误消息 ../xxx/yyy/zzz/your_file.c: No such file or directory
。要解决此问题,请将源代码复制到运行 GDB 的主机(示例中的 host2
)。确保保存原始代码层次结构。使用 GDB 命令 directory
通知 GDB 源代码的位置
host2 上的 gdb
host2~$> gdb-multiarch -q /tmp/my_riscv.elf
Reading symbols from /tmp/my_riscv.elf...
(gdb) b foo
Breakpoint 1
at 0x4000016c
: file ../xxx/yyy/zzz/my_file.c, line 182
.
(gdb) target remote host1:1981
Remote debugging using host1:1981
0x0800000000000000
in
?? ()
(gdb) c
Continuing.
[New Thread 1.32769
]
[Switching to Thread 1.32769
]
Thread 2
hit Breakpoint 1
, foo (thread_arg=5728
) at ../xxx/yyy/zzz/my_file.c:182
182
../xxx/yyy/zzz/my_file.c: No such file or directory.
(gdb) directory /tmp/apps/
Source directories searched: /tmp/apps:$cdir:$cwd
(gdb) list
179
struct flexio_dev_thread_ctx *dtctx;
180
uint64_t dev_errno;
181
182
print_sim_str("=====> NET event handler started\n"
, 0
);
183
184
flexio_dev_print("Hello GDB user\n"
);
185
注意 GDB 报告的确切路径。命令 directory
的参数应指向此路径的起点。例如,如果 GDB 查找 ../xxx/yyy/zzz
,并且您将源代码放置在本地目录 /tmp/copy_of_worktree
中,则命令应为 (gdb) directory /tmp/copy_of_worktree/xxx/
,而不是 (gdb) directory /tmp/copy_of_worktree/
。
有时,*.elf
文件提供来自根目录的全局路径。在这种情况下,请使用命令 set substitute-path <from> <to>
。例如,如果文件 /foo/bar/baz.c
已移动到 /mnt/cross/baz.c
,则命令 (gdb) set substitute-path /foo/bar /mnt/cross
指示 GDB 将 /foo/bar
替换为 /mnt/cross
,这允许 GDB 找到文件 baz.c
,即使该文件已被移动。
有关指定源目录的更多示例,请参阅 GDB 文档的此页面。
Core Dump 用法
如果代码即使在项目的宿主机端正确实现的情况下仍遇到致命错误,则会保存 core dump,从而可以分析 core。它应准确指向发生致命错误的位置。命令 backtrace
可用于检查内存及其寄存器。更改帧以查看回溯列表中每个函数的局部变量
gdb
$> gdb-multiarch -q -c crash_demo.558184
.core /tmp/my_riscv.elf
Reading symbols from /tmp/my_riscv.elf...
[New LWP 1
]
#0
0x000000004000126e
in
read_test (line=153
, ptr=0x30
) at /xxx/yyy/zzz/my_file.c:109
109
val = *(volatile uint64_t *)ptr;
(gdb) bt
#0
0x000000004000126e
in
read_test (line=153
, ptr=0x30
) at /xxx/yyy/zzz/my_file.c:109
#1
0x000000004000031a
in
tlb_miss_test (op_code=1
) at /xxx/yyy/zzz/my_file.c:153
#2
0x0000000040000144
in
test_thread_err_events_entry_point (h2d_daddr=3221258560
) at /xxx/yyy/zzz/my_file.c:588
#3
0x00000000400013fc
in
_dpacc_flexio_dev_arg_unpack_test_err_events_dev_test_thread_err_events_entry_point (argbuf=0xc0008228
, func=0x400000b0
<test_thread_err_events_entry_point>)
at /tmp/dpacc_xExkvE/test_err_events_dev.dpa.device.c:67
#4
0x0000000040001680
in
flexio_hw_rpc (host_arg=3221258752
) at /local_home/www/flexio-sdk/libflexio-dev/src/flexio_dev_entry_point.c:75
#5
0x0000000000000000
in
?? ()
Backtrace
stopped: frame did not save the PC
(gdb) frame 4
#4
0x0000000040001680
in
flexio_hw_rpc (host_arg=3221258752
) at /local_home/igorle/flexio-sdk/libflexio-dev/src/flexio_dev_entry_point.c:75
75
retval = unpack_cb(&data_from_host->func_params.arg_buf,
(gdb) p /x *data_from_host
$2
= {poll_lkey = 0x1ff2b1
, window_id = 0x3
, poll_haddr = 0x55dc0f40b900
, entry_point = 0x400013d8
, func_params = {func_wo_pack = 0x0
, dev_func_entry = 0x400000b0
, arg_buf = 0xc0008140
}}
(gdb)
优化代码的调试
通常,编译和运行的是高度优化的代码。
可以考虑两种类型的代码错误
逻辑错误
与优化相关的错误
逻辑错误(例如,使用 &
而不是 &&
)在代码的非优化版本中重现。与优化相关的错误(例如,忘记 volatile 分类、不使用内存屏障)仅影响优化。非优化代码更易于使用 GDB 进行跟踪,因为每个 C 指令都直接转换为汇编代码。
检查问题是否可以在非优化代码上重现是一种良好的实践。这有助于观察应用程序流程
Bash
$> build.sh -O 0
对于跟踪此代码,使用 GDB 命令 next
和 step
应该足够了。
但是,如果问题只能在优化代码上重现,则应开始调试它。这将需要读取反汇编代码并使用 GDB 命令 stepi
,因为要准确理解执行了哪个 C 代码行变得具有挑战性。
高级 RISC-V 命令的反汇编
DPA 核心在具有扩展指令集的 RISC-V CPU 上运行。GDB 可能不熟悉其中一些指令。因此,asm
视图模式显示数字而不是反汇编。在这种情况下,建议手动反汇编您的 RISC-V 二进制代码。将 dpa-objdump
实用程序与附加选项 --mcpu=nv-dpa-bf3
一起使用。
bash
$> dpa-objdump -sSdxl --mcpu=nv-dpa-bf3 my_riscv.elf > my_riscv.asm
以下屏幕截图显示了差异
