如何在Linux下用C和Python编写的多线程CUDA应用程序中检测英特尔CPU上的寄存器崩溃



我目前正在尝试调试一个具有许多不同模块的大型应用程序,其中一些模块是用C编写的,另一些是用Python编写的。它同时使用多线程和CUDA。它运行在Linux下的现代英特尔处理器上。

目前,我有一个测试用例,它在一个循环中运行了大约一个小时,然后出现断言错误。查看堆栈跟踪,它显示我调用的g_signal_disconnect(obj, sig)具有sig的有效值,但g_signal_disconnect看到的是sig的无意义值。在为调用设置的寄存器和实际调用之间,似乎发生了一些事情来更改保存sig值的%rsi寄存器。也就是说,调用者的堆栈帧在局部变量和寄存器中显示了sig的正确值,但被调用者看到的却是一个大的随机数。我猜是其他任务运行或外部中断导致了问题,但这完全是猜测。

这个错误是一致的,因为它总是这个特定的调用被破坏,但它只在这个调用的数千次执行中随机发生一次。我是在本地运行、在gdb下运行还是在valgrind下运行似乎也无关紧要。这种情况仍然存在。

因为它是一个正在更改的寄存器,所以我无法让gdb在它上面设置一个观察点来查看是什么在更改它。gdb也无法在多线程环境中反向运行代码。

因为它是一个CUDA应用程序,所以我无法使用rr调试器来记录导致该问题的确切指令流。

虽然我可以在valgrind下运行程序并得到一些结果,但它只是告诉我,当我去使用它时,sig值是未定义的,而不是当某些东西使它未定义时。valgrind也没有显示出任何可能是罪魁祸首的内存或多任务错误。

现在,我可以完全访问发生错误的模块的源代码,所以我可以在任何有意义的情况下对其进行检测,或者只要这些编译选项与它运行的linux堆栈的其余部分兼容,就可以重新编译它,所以我可能会做些什么,但我不知道该怎么做。

只要找到一些方法来了解哪些任务在寄存器粉碎窗口期间运行和/或中断,就会大大缩小范围,但我也不知道如何获取这些信息。

有人知道有什么工具、技巧、技巧或其他什么可以让我当场抓住抢牌者吗?一旦我知道应该归咎于什么程序,就应该有可能修复它

好的,感谢大家的帮助。为了解决我提出的实际问题,目前最好由能够记录和回放多线程指令流的调试器来解决这类问题。RR Debugger做到了这一点,并且是开源的,但不支持CUDA。Undo UDB是商业性的,部分支持CUDA。目前,在类似情况下,这是你的最佳选择(尽管在我的情况下,CUDA的支持不足(。这两个都是GDB录音设备的附加组件。

现在,关于最终被发现并修复的实际错误,它并不是注册腐败,而是看起来像。它是一个数据竞赛问题。犯了这个特别的错误我很尴尬,但事实就是这样

void signal_setup(...)
{ struct signal_data * data = malloc(sizeof(struct signal_data));
data->a = ...
data->b = ...
data->sig = g_signal_connect(obj, "sig", signal_cb, data,...);
...
}
void signal_cb( GObject * obj, void * user_data )
{ struct signal_data * data = user_data;
g_signal_disconnect(obj, data->sig);
...
free(data);
}

事实证明,大约每200000次呼叫中就有一次,信号将在对g_signal_connect的呼叫及其存储在数据中的信号id之间被触发;sig。这将导致从数据中提取值->回调中的sig是随机垃圾,g_signal_disconnect会(正确地(抱怨这一点。

但是,由于回调与signal_setup例程在不同的线程中,signal_setups将在几毫秒后完成,并完成结构signal_data的填充,以便它是正确的。结果是,当我在调试器中查看堆栈帧时,数据结构具有有效的数据,但从该结构中读取的寄存器是垃圾。因此,我假设在一个狭窄的窗口中登记腐败。

直到我对每个信号设置和每个信号回调进行了带时间戳的日志记录,并在设置之前、崩溃之前看到了回调,我才发现真正的错误。

在这种情况下可以使用的另一种可能方法是使用systemtap来监视任务切换和内存更改等情况。由于它完全可以编写脚本,因此可以随心所欲地精确监控。有一个学习曲线来弄清楚它的脚本语言,但它是解决这类复杂问题的绝佳工具。

相关内容

最新更新