用Visual Studio进行GPU开发时,断点无法进入CUDA核函数进行调试,因为默认断点在CPU设备上的程序生效,无法访问GPU设备上的内存空间,这个时候需要借助GPU调试工具让断点能够进入CUDA核函数进行调试。网上这方面的资料甚少,要摸索清楚其中的细节需要花很多精力,所以我总结了一份资料以供参考。
1、CUDA调试工具 – Nsight Visual Studio Edition
前往官网进行下载 Nsight Visual Studio Edition调试工具
![图片[1]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-21_163723_257.png)
注意依赖的CUDA版本
![图片[2]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-21_164104_757.png)
鼠标右键查看查看NVIDIA 显示驱动程序版本
![图片[3]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-21_164128_840.png)
所以我这里下载的是2025.2.1版本的Nsight Visual Studio Edition,下载完之后打开Visual Studio,显示如下代表安装成功(我用的Visual Studio2022,不同版本显示地方可能不同)
![图片[4]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/d6a6eaf3-517b-4169-bf84-8e6dd6f1e462.png)
2、sln项目调试CUDA核函数
配置CUDA依赖(和调试工具无关,这是CUDA程序必须的依赖项)
![图片[5]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/f2b6a6c5-94c2-43fc-8ca5-b716f70cdbc6.png)
勾选下面的CUDA依赖项
![图片[6]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-21_165429_094.png)
右键单击包含的.cu
文件,然后选择属性
,将项目类型更改为CUDA C/C++
。
![图片[7]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-08-22%20100415.png)
![图片[8]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-08-22%20100500.png)
操作完之后,右键项目选择“属性”会显示以下项
![图片[9]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-21_170138_382.png)
Debug模式直接调试,无需配置
![图片[10]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-08-21%20165003.png)
Release模式下调试,会显示以下情况,通常是由于编译配置或调试设置不正确导致的。
![图片[11]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-21_170250_136.png)
右键项目选择“属性”,将以下项改为“是”即可,使其能够生成设备代码(核函数)的调试信息
![图片[12]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-21_170408_893.png)
3、cmake项目调试CUDA核函数
需要在cmakelists.txt中增加以下内容
# CUDA调试配置
target_compile_options(BinaryClassify PRIVATE
$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:CUDA>>:-G -g -O0>
$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:CXX>>:-O0>
)
核心作用:
其中-G -g -O0
的意思:-G
:CUDA 编译器(nvcc)专用标志,生成 GPU 核函数(设备代码)的调试信息(没有它,调试器无法识别核函数的断点位置)。-g
:生成 CPU 端代码(主机代码)的调试信息(确保主机端 C++ 代码的断点正常工作)。-O0
:禁用编译器优化(优化会导致源码行与实际执行代码位置不匹配,调试时可能跳行或断点失效)。
完整的CMakeLists.txt文件参考(这是我实际项目中的CMakeLists.txt,根据自己的情况修改)
cmake_minimum_required (VERSION 3.18)
# 变量
# 检查环境变量是否设置
if(NOT DEFINED ENV{CUDA_PATH})
message(FATAL_ERROR "请设置 CUDA_PATH 环境变量指向 CUDA 安装目录")
endif()
if(NOT DEFINED ENV{TENSORRT_PATH})
message(FATAL_ERROR "请设置 TENSORRT_PATH 环境变量指向 TensorRT 安装目录")
endif()
if(NOT DEFINED ENV{OPENCV_PATH})
message(FATAL_ERROR "请设置 OPENCV_PATH 环境变量指向 OpenCV 根目录")
endif()
set(CUDA_PATH $ENV{CUDA_PATH})
set(TENSORRT_PATH $ENV{TENSORRT_PATH})
set(OPENCV_PATH $ENV{OPENCV_PATH})
project (BinaryClassify LANGUAGES CXX CUDA)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CXX_STANDARD 17)
# 添加 CUDA 源文件
file(GLOB_RECURSE CURRENT_CUDA_SOURCES
"CUDA_FUNC/*.cuh"
"CUDA_FUNC/*.cu"
)
# 定义可执行目标
add_executable (BinaryClassify
"BinClassify/main.cpp"
"BinClassify/BinaryClassify.cpp"
"BinClassify/OnnxToTensorRT.cpp"
${CURRENT_CUDA_SOURCES}
"common/utils.h" "common/utils.cpp")
# CUDA调试配置
target_compile_options(BinaryClassify PRIVATE
$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:CUDA>>:-G -g -O0>
$<$<AND:$<CONFIG:Debug>,$<COMPILE_LANGUAGE:CXX>>:-O0>
)
# 包含目录
target_include_directories(BinaryClassify PRIVATE
"${CUDA_PATH}/include"
"${TENSORRT_PATH}/include"
"${OPENCV_PATH}/build/include"
"${OPENCV_PATH}/build/include/opencv2"
"${CMAKE_CURRENT_SOURCE_DIR}/common"
"${CMAKE_CURRENT_SOURCE_DIR}/ThirdParty"
"${CMAKE_CURRENT_SOURCE_DIR}/CUDA_FUNC"
)
# 指定库文件搜索路径
target_link_directories(BinaryClassify PRIVATE
"${TENSORRT_PATH}/lib"
"${OPENCV_PATH}/build/x64/vc15/lib"
"${CUDA_PATH}/lib/x64"
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_libraries(BinaryClassify PRIVATE
opencv_world450d.lib
nvinfer.lib
nvinfer_plugin.lib
nvonnxparser.lib
nvparsers.lib
cudart.lib
)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
target_link_libraries(BinaryClassify PRIVATE
opencv_world450.lib
nvinfer.lib
nvinfer_plugin.lib
nvonnxparser.lib
nvparsers.lib
cudart.lib
)
endif()
# 自动生成CUDA设备代码
set_target_properties(BinaryClassify PROPERTIES
CUDA_SEPARABLE_COMPILATION ON
)
调试界面如下,断点进入了cuda核函数,大功告成!
![图片[13]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-21_172037_870.png)
4、工具使用教程
可以参考 Nsight Visual Studio Edition官方文档
4.1、Break On Launch选项
其核心作用是:让程序在启动时立即暂停(中断)执行,方便开发者从程序入口开始逐步调试,尤其是针对启动阶段的初始化逻辑(包括 CUDA 上下文创建、设备初始化等早期操作)
![图片[14]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-08-21%20172416.png)
使用场景举例:
- 调试依赖启动顺序的逻辑(如先初始化 TensorRT 引擎,再调用 CUDA 核函数),确保每一步初始化都按预期执行。
- 当你的 CUDA 程序在启动后很快崩溃(如未进入
main
函数就报错),启用Break On Launch
可以捕获崩溃前的初始状态; - 需验证 CUDA 上下文是否在程序启动时正确创建,可在中断后通过 Nsight 的 CUDA 调试窗口(如 “CUDA Contexts”)查看设备状态;
4.2、步进行为
当使用 “syncthreads()
同步点。
调试时,step
会冻结其他线程束(仅跟踪单个线程束),但可能卡在syncthreads()
;step over
会让当前线程块内所有线程束一起推进,确保syncthreads()
能正常完成,避免调试时的同步阻塞。这是针对 CUDA 并行代码(依赖线程同步)的特殊调试优化。
4.3、冻结选项
在 CUDA 核函数调试中,“冻结”(Frozen)指的是调试器暂停特定线程 / 线程束 / 线程块的执行状态,使其暂时停止推进指令、修改寄存器或内存数据,仅保留当前的执行上下文(如寄存器值、变量状态、程序计数器位置等)。这一机制是并行调试的核心手段,目的是在多线程并行执行的复杂环境中,隔离并聚焦于需要调试的目标线程(或线程束),避免其他并行单元的执行干扰调试观察。
![图片[15]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202025-08-22%20133914.png)
设置 | 对“运行”命令的作(继续、F5 或运行到光标) | 对“单步”命令的作(单步执行、单步执行或单步执行) |
---|---|---|
调度程序锁定恢复全部 Scheduler Locking Resume All | 没有什么是冻结的。 | 没有什么是冻结的。 |
调度程序锁定恢复块 Scheduler Locking Resume Block | 当前块之外的所有线程束都被冻结。 | 当前块之外的所有线程束都被冻结。 |
调度程序锁定恢复线程束 Scheduler Locking Resume Warp | 除当前线程束外,所有线程束都被冻结。 | 除当前线程束外,所有线程束都被冻结。 |
调度程序锁定步进块 Scheduler Locking Step Block | 没有什么是冻结的。 | 当前块之外的所有线程束都被冻结。 |
调度器锁定步长线程束(默认) Scheduler Locking Step Warp (default) | 没有什么是冻结的。 | 除当前线程束外,所有线程束都被冻结。 |
4.4、“线程”列的颜色图例
![图片[16]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-22_155500_188.png)
4.5、寄存器
在 CUDA 调试器中,“Registers”(寄存器)视图用于展示当前 GPU 寄存器的状态,帮助开发者观察线程执行过程中寄存器值的变化(
- SASS 寄存器反映硬件执行的真实状态;
- Predicate 寄存器揭示线程的条件执行状态;
- PTX 和 PTX loc 寄存器则从中间代码层面关联高级语言与底层执行,便于理解编译器行为。
![图片[17]-CUDA核函数的调试-阿尔法欧欧](/robingo/%E6%96%87%E7%AB%A0%E8%B5%84%E6%BA%90/2025_08/CUDA%E6%A0%B8%E5%87%BD%E6%95%B0%E8%B0%83%E8%AF%95/wechat_2025-08-22_164751_498.png)
2、文章版权归作者所有,未经允许请勿转载
3、本站资源定期维护,如发现链接失效,请与作者联系
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理
暂无评论内容