近期,Meta AI团队在生产PyTorch AI模型时遇到了一个难题。这一问题由CUDA非法内存访问引起,号称集结了Meta全公司最牛的AI工程师才搞定,这篇博客记录了他们使用CUDA的core dump来确定报错位置所使用的技巧和实践。
作者|Zachary DeVito
翻译|贾川、程浩源
如果GPU读取了无效内存,那么CUDA API将会开始从发生错误的地方开始,后续所有API调用都会返回cudaErrorIllegalAddress:
设备在无效内存地址上使用了加载或存储指令。这使得进程处于不一致的状态,任何后续的CUDA工作都将返回相同的错误。若要继续使用CUDA,进程必须终止并重新启动。
因为CUDA kernel是从CPU异步启动,所以在启动异常kernel的地方不会报告此错误,而是在GPU上实际发生异常并传播到CPU之后的任何CUDA API调用时报告此错误。
当然,要是使用CUDA_LAUNCH_BLOCKING=1环境变量,CUDA就会在kernel启动后运行完成才返回,但这会使得程序运行明显变慢,可能会改变报错时机,以致某些不确定性问题不再被触发。
此外,如果有多个线程使用CUDA API,cudaErrorIllegalAddress可能首先在另一个线程上报错,而不是在启动线程上报错。因此,即使在CUDA_LAUNCH_BLOCKING=1的情况下,我也不信任堆栈跟踪呈现的信息。
相反,对于“非法地址(illegal address)”这一bug,我们希望能找到更多、更准确的报错原因。类似于其他处理器,当故障发生时,GPU上的SM会记录有关故障指令的信息。
不幸的是,我意识到没有进程内的方法可以获取这类信息。我们只能在运行之前,通过将cuda-gdb或cuda-memcheck附加到进程中来访问此类信息。但这对于那些发生率很低的bug来说,在这种模式下重新运行这个进程来重现bug是不切实际的。
幸运的是,通过设置环境变量CUDA_ENABLE_COREDUMP_ON_EXCEPTION=1,我们可以使CUDA在发生异常后生成core dumps来呈现GPU的状态,然后用cuda-gdb来检查该文件。
本文讨论了如何从这些core dumps中生成提取信息,以便在没有调试信息的情况下,也能恢复诸多信息,比如参数值和出错指令等。
1
生成core dumps
在有故障的进程上设置 CUDA_ENABLE_COREDUMP_ON_