2. 简介
本文档描述了CUDA库中可供任何调试器使用的集合例程和数据结构的API。
-
性能得到显著提升,包括与调试器的交互以及被调试应用程序的运行性能。
-
cubin的格式已更改为ELF,因此大多数对调试编译的限制已被取消。关于新对象格式的更多信息如下所示。
2.1. Debugger API
CUDA调试器API的开发遵循以下原则:
-
策略无关
-
显式
-
公理化的
-
可扩展性
-
面向机器
设备主要有两种"模式":停止或运行。我们通过suspendDevice和resumeDevice在这些模式间显式切换,不过机器也可能自行暂停,例如当遇到断点时。
只有在停止状态下,我们才能查询机器的状态。Warp状态包括当前正在运行的函数、所在的块、哪些通道有效等信息。
从CUDA 6.0开始,如果在未首先调用suspendDevice入口点以确保设备停止的情况下调用调试API中的状态收集函数,将返回CUDBG_ERROR_RUNNING_DEVICE错误。
调试API的客户端应在处理CUDBGEvent前暂停所有设备。只有在执行了通过CUDBGAPI_st::setNotifyNewEventCallback()设置的通知回调后,才能确保返回有效的CUDBGEvent。如果在通过CUDBGAPI_st::setNotifyNewEventCallback()设置的通知回调内部调用任何调试API入口点,都将返回CUDBG_ERROR_RECURSIVE_API_CALL错误。
2.2. ELF与DWARF格式
CUDA应用程序以ELF二进制格式编译。
从CUDA 6.0开始,DWARF设备信息通过调用CUDBGAPI_st::getElfImageByHandle API获取,该调用使用从CUDBGEvent类型为CUDBG_EVENT_ELF_IMAGE_LOADED的事件中暴露的句柄。这意味着在CUDA驱动程序加载完成之前,运行时无法获取该信息。DWARF设备信息的有效期持续到其被卸载为止,此时会触发类型为CUDBG_EVENT_ELF_IMAGE_UNLOADED的CUDBGEvent事件。
在CUDA 5.5及更早版本中,DWARF设备信息作为CUDBGEvent类型CUDBG_EVENT_ELF_IMAGE_LOADED的一部分返回。CUDBGEvent55中提供的指针是只读指针,指向由调试API管理的内存。所指向的内存在加载CUDA上下文的生命周期内隐式限定范围。在上下文被销毁后访问返回的指针会导致未定义行为。
DWARF设备信息包含除代码内存外所有设备内存区域的物理地址。地址类别字段(DW_AT_address_class)为所有设备变量设置,用于指示内存段类型(ptxStorageKind)。必须使用几个特定于段的API调用来访问这些物理地址。
<2><321>: Abbrev Number: 18 (DW_TAG_formal_parameter)
DW_AT_decl_file : 27
DW_AT_decl_line : 5
DW_AT_name : res
DW_AT_type : <2c6>
DW_AT_location : 9 byte block: 3 18 0 0 0 0 0 0 0 (DW_OP_addr: 18)
DW_AT_address_class: 7以上显示变量'res'的地址类别为7(ptxParamStorage)。其位置信息表明它位于参数内存段的地址18处。
默认情况下,局部变量不再溢出到本地内存。DWARF现在包含所有变量的寄存器映射和存活期信息。某些情况下变量仍可能溢出到本地内存,这些信息全部以ULEB128编码格式存储在DWARF中(作为DW_AT_location属性中的DW_OP_regx栈操作)。
以下是一个位于本地寄存器中的变量的典型DWARF条目:
<3><359>: Abbrev Number: 20 (DW_TAG_variable)
DW_AT_decl_file : 27
DW_AT_decl_line : 7
DW_AT_name : c
DW_AT_type : <1aa>
DW_AT_location : 7 byte block: 90 b9 e2 90 b3 d6 4 (DW_OP_regx: 160631632185)
DW_AT_address_class: 2这表明变量'c'具有地址类别2(ptxRegStorage),其位置可通过解码ULEB128值DW_OP_regx:160631632185来定位。有关如何解码该值以及如何获取在特定设备PC范围内保存此变量的物理寄存器的信息,请参阅cuda-gdb源代码包中的cuda-tdep.c文件。
2.3. ABI兼容性支持
如需了解更多信息,请参阅标题为《Fermi ABI:应用二进制接口》的ABI文档。
2.4. 异常报告
异常报告仅在Fermi架构(sm_20或更高版本)上受支持。
2.5. 附加与分离
调试客户端必须按照以下步骤连接到正在运行的CUDA应用程序:
-
附加到与CUDA应用程序对应的CPU进程。此时应用程序的CPU部分将被冻结。
-
检查CUDBG_IPC_FLAG_NAME变量是否可从应用程序的内存空间访问。如果不可访问,则意味着应用程序尚未加载CUDA驱动程序,此时对应用程序的附加操作已完成。
-
动态(非优先)调用函数cudbgApiInit(),传入参数"2",即"cudbgApiInit(2)",例如在Linux系统上使用ptrace(2)。这将从应用程序中分叉出一个辅助进程,帮助附加到CUDA进程。
-
确保CUDA调试API的初始化已完成,或等待API初始化成功(即调用"initialize()" API方法直至成功)。
-
调用"initializeAttachStub()" API来初始化之前从应用程序分叉出来的辅助进程。
-
从应用程序的内存空间中读取CUDBG_RESUME_FOR_ATTACH_DETACH变量的值:
-
如果该值为非零,则恢复CUDA应用程序以便收集更多关于该应用程序的数据并发送至调试器。当应用程序恢复时,调试客户端可以预期会从CUDA应用程序接收各种CUDA事件。一旦所有状态收集完毕,调试客户端将收到CUDBG_EVENT_ATTACH_COMPLETE事件。
-
如果值为零,则表示没有更多附加数据需要收集。在应用程序的进程空间中设置CUDBG_IPC_FLAG_NAME变量为1,这将启用来自CUDA应用程序的更多事件。
-
-
此时,连接到CUDA应用程序的过程已完成,属于该CUDA应用程序的所有GPU将被暂停。
调试客户端必须执行以下步骤以从正在运行的CUDA应用程序中分离:
-
检查CUDBG_IPC_FLAG_NAME变量是否可从应用程序的内存空间访问,并确认CUDA调试API已初始化。如果任一条件不满足,则将应用程序视为仅CPU运行并与之分离。
-
接下来,调用"clearAttachState" API接口,为CUDA调试API的分离操作做好准备。
-
在应用程序的内存空间中动态(非直接)调用函数cudbgApiDetach(),例如在Linux上使用ptrace(2)。这将使CUDA驱动设置分离状态。
-
从应用程序的内存空间中读取CUDBG_RESUME_FOR_ATTACH_DETACH变量的值。如果该值非零,则调用"requestCleanupOnDetach" API。
-
在应用程序的内存空间中,将CUDBG_DEBUGGER_INITIALIZED变量设置为0。这样可以确保如果调试客户端将来重新附加到应用程序时,调试器会从头开始重新初始化。
-
如果在步骤4中发现CUDBG_RESUME_FOR_ATTACH_DETACH变量的值非零,则删除所有断点并恢复CUDA应用程序运行。这允许CUDA驱动程序在调试客户端与其分离前执行清理操作。清理完成后,调试客户端将收到CUDBG_EVENT_DETACH_COMPLETE事件。
-
将应用程序内存空间中的CUDBG_IPC_FLAG_NAME变量设置为零。这将阻止CUDA应用程序向调试器发送更多回调。
-
客户端随后必须完成CUDA调试API的初始化。
-
最后,从CUDA应用的CPU部分分离。此时,属于该CUDA应用的所有GPU都将恢复运行。