CUDA debug方法和一些问题总结(越界访问,debug与release结果不一致问题等)

现象

bt的堆栈信息

standard io上的错误输出

从报错信息上看是非法的内存访问,但是报错的位置不一定是真实的位置,因为GPU都是异步发起的,错误可能会被在后面的op捕捉。例如cudaEventDestory:

 debug方式

思维方式  

  1. 复现,解决问题一定要复现问题,不能复现的问题不能确定正真解决。所以首先要做的是复现。
  2. 定位,定位范围是逐渐缩小,优先排查自定义的代码。对于cuda-kernel,默认都是异步的操作,很多时候捕获错误的位置在抛出错误位置的后面。这种情况可以在可能出错的op后面加上下面代码:
 CUDA_RETURN_IF_ERROR(cudaStreamSynchronize(stream));

 cuda-memcheck

命令行,使用cuda-memcheck的工具memcheck,当然它还有另外的三个工具,详情看文档。

cuda-memcheck --tool memcheck [application application-option]

结果返回发生越界的thread号和block号。 

然后通过block和thread的坐标信息,借助cuda-gdb工具,定位到具体的位置。

cuda-gdb

在编译时,给nvcc加上-g -G标识(cmake中是CMAKE_CUDA_FLAGS),就可以得到device的debug符号信息,其中的-G是标识开启device的debug编译。

set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -g -G")

Debug的操作指令: 

# switch to the forced thread and block
cuda kernel 0 block (3,103,0) thread(0,0,0)

# condition breakpoint
b file:498 if blockIdx.y==103 && blockIdx.x==3

# set conditions for an existing breakpoint
condition 1 blockIdx.x == 0 && n > 3

 找到了具体越界的位置。

cuda-gdb中打印数据

device上的地址或者指针前加上(@global type*),其他指令相同的方式使用。

x/249dg ((@global long *)0x7ff7ef111400)

 在shared memory上打印数据的方法。

x/2048fh (@generic half * const) 0x7fff55004000

compute-sanitizer

对于debug和release版本结果不一致的问题,debug无法复现问题,这个时候需要借助一些工具,compute-sanitizer是一个非常有用的工具。其中有含有四个子工具,具体见文档

debug与release code的差异:

1. warp的schedule方式不同,会有一些同步的差异;

2. 资源量不同,所以release版本可能有buffer溢出的问题;

3. 速度差异,release会更快,结合了更多的优化,这些优化会带来一些问题,同时更快的运行速度结合1.中的差异,也会带来脏读,脏写的问题。

结合具体问题使用下面code来排查:

compute-sanitizer --tool racecheck ./test

printf定位具体位置

对于代码比较简单,但是并发度很高的cuda-kernel-func,采用这个方式比较高效。但是需要注意:

  1.  ort里的cuda代码默认是开启了O3的优化等级的。如果注释掉一些代码,相关的一些代码可能被优化掉。解决方法:把删除的代码中的变量,用printf加上,可以屏蔽这个优化,当然也可以开启nvcc的debug模式。
  2. cuda-kernel里的printf要注意数据的字节数,例如,int64的类型,在打印的时候需要加l(%ld),不然会引起错位,该字符串后面的输出,都会出现错误。

参考文档

https://on-demand.gputechconf.com/gtc/2014/presentations/S4580-cuda-gdb-cuda-memcheck-debugging-tools.pdf

https://ece.northeastern.edu/groups/nucar/Analogic/cuda-gdb.pdf

Compute Sanitizer User Manual :: Compute Sanitizer Documentation

### CUDA 编程中的调试方法工具 #### 使用 NVIDIA CUDA-GDB 进行调试 NVIDIA 提供了一个专门用于 CUDA 应用程序的 GDB 版本——CUDA-GDB。此工具允许开发者设置断点、单步执行以及查看变量值等功能,从而有效地定位修复代码中的问题[^1]。 为了在 CLion 中利用 CUDA-GDB 的功能,可以通过在其 ToolChains 设置中加入 cuda-gdb 来完成初步配置。此外,在项目构建过程中需明确指定 nvcc 作为编译器以确保兼容性正确性。 #### 内核调试内存调试分离策略 针对复杂的 GPU 计算任务,通常会将整个调试过程划分为两大部分:内核调试内存调试。前者专注于分析并解决发生在设备端核心逻辑内的异常情况;后者则着重于检测可能存在的非法地址访问或者越界操作等问题[^2]。 #### 错误处理机制增强可靠性 除了传统的调试手段外,还可以通过引入系统的错误检查流程进一步提升程序稳定性。具体做法是在每一次调用 CUDA API 后立即跟进一次状态查询命令 (如 `cudaGetLastError`) ,一旦发现返回值正常即刻记录相关信息以便后续排查工作开展得更加顺利[^4]。 另外值得注意的是,`cuda-memcheck` 是另一个非常有用的辅助组件,它能够自动扫描潜在的存储管理漏洞比如未初始化读取或是超出分配范围写入等危险行为,并向用户提供清晰直观的结果报告。 ```python def check_cuda_error(): err = cudaGetLastError() if err != cudaSuccess: print(f"CUDA error: {cudaGetErrorString(err)}") ``` 以上就是关于如何有效实施 CUDA 开发环境下 Debugging 方法的一些基本指导原则技术要点概述。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值