本节介绍CPU域调试的方法:CPU侧验证核函数,gdb调试、使用printf命令打印。当前SIMT编程场景不支持。
在非昇腾设备上,开发者可以利用CPU仿真环境先行进行算子开发和测试,并在准备就绪后,利用昇腾设备进行加速计算。在编译与运行章节,我们已经介绍了算子Kernel程序NPU域的编译运行。相比于NPU域的算子运行逻辑,CPU域调试将算子Kernel程序以Host程序的形式进行编译,此时算子Kernel程序链接CPU调测库,执行编译生成的可执行文件,可以完成算子CPU域的运行验证。CPU侧的运行程序,通过GDB通用调试工具进行单步调试,可以精准验证程序执行流程是否符合预期。
推荐使用CMake编译方式,可在最小化修改的情况下快速开启CPU域孪生调试功能。
1 2 3 |
#ifdef ASCENDC_CPU_DEBUG #include "cpu_debug_launch.h" #endif |
cmake -B build -DCMAKE_ASC_RUN_MODE=cpu -DCMAKE_ASC_ARCHITECTURES=dav-2201
cpu表示开启CPU域编译,dav-后为NPU架构版本号,请根据实际情况进行填写。
其他CMakeLists.txt项目配置通过CMake编译进行编写。
为了实现CPU域与NPU域代码归一,框架在CPU域中仅对部分acl接口进行适配,开发者在使用CPU域调测功能时,仅支持使用如下acl接口,并且不支持用户自行链接ascendcl库:
可使用gdb单步调试算子计算精度。由于cpu调测已转为多进程调试,每个核都会拉起独立的子进程,故gdb需要转换成子进程调试的方式。针对耦合架构,每个AI Core会拉起1个子进程。针对分离架构,默认每个AI Core会拉起3个子进程,1个Cube,2个Vector。
启动gdb,示例中的add_custom_cpu为CPU域的算子可执行文件,参考修改并执行一键式编译运行脚本,将一键式编译运行脚本中的run-mode设置成cpu,即可编译生成CPU域的算子可执行文件。
gdb --args add_custom_cpu // 启动gdb,add_custom_cpu为算子可执行文件 (gdb) set follow-fork-mode child
在gdb启动后,首先设置调试模式为只调试一个进程,挂起其他进程。设置的命令如下:
1
|
(gdb) set detach-on-fork off |
查看当前调试模式的命令为:
1
|
(gdb) show detach-on-fork |
中断gdb程序要使用捕捉事件的方式,即gdb程序捕捉fork这一事件并中断。这样在每一次起子进程时就可以中断gdb程序。设置的命令为:
1
|
(gdb) catch fork |
当执行r后,可以查看当前的进程信息:
1 2 3 |
(gdb) info inferiors Num Description * 1 process 19613 |
可以看到,当第一次执行fork的时候,程序断在了主进程fork的位置,子进程还未生成。
执行c后,再次查看info inferiors,可以看到此时第一个子进程已经启动。
1 2 3 4 |
(gdb) info inferiors Num Description * 1 process 19613 2 process 19626 |
这个时候可以使用切换到第二个进程,也就是第一个子进程,再打上断点进行调试,此时主进程是暂停状态:
1 2 3 4 5 6 |
(gdb) inferior 2 [Switching to inferior 2 [process 19626] ($HOME/demo)] (gdb) info inferiors Num Description 1 process 19613 * 2 process 19626 |
请注意,inferior后跟的数字是进程的序号,而不是进程号。
如果遇到同步阻塞,可以切换回主进程继续生成子进程,然后再切换到新的子进程进行调试,等到同步条件完成后,再切回第一个子进程继续执行。
如下是调试一个单独子进程的命令样例:
gdb --args add_custom_cpu set follow-fork-mode child break add_custom.cpp:45 run list backtrace print i break add_custom.cpp:56 continue display xLocal quit
1 2 |
printf("xLocal size: %d\n", xLocal.GetSize()); printf("tileLength: %d\n", tileLength); |