什么会导致 std::difftime 造成 SIGBUS 崩溃?
What could cause std::difftime to create a SIGBUS crash?
今天,我不得不惊恐地意识到我的 C++ 模拟程序在 运行 后崩溃了 12 天,就在它结束前几行,除了一个(截断的)核心转储,我什么都没有。
用 gdb 分析核心转储显示,
Program terminated with signal SIGBUS, Bus error.
并且崩溃发生在我的代码的以下行:
seconds = std::difftime(stopTime, startTime); // seconds is of type double
变量 stopTime
和 startTime
的类型为 std::time_t
,我能够在崩溃时从核心转储中提取它们的值:
startTime: 1426863332
stopTime: 1427977226
difftime 调用上方的堆栈跟踪如下所示:
#0 0x.. in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
#1 0x.. in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2
我写了一个小程序重现错误,但没有成功。仅使用上述值调用 std::difftime(stopTime, startTime)
不会导致 SIGBUS 崩溃。当然,我不希望这种情况再次发生。我之前曾多次成功执行过相同的程序(尽管使用不同的参数),执行时间相当。是什么导致了这个问题,以后我该如何预防?
这是一些额外的系统信息。
GCC: (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388]
Linux Kernel: 3.11.10-25-desktop, x86_64
C++ standard library: 6.0.18
编辑
这里有更多的上下文。首先,完整的堆栈跟踪(省略号 [..]
我的):
#0 0x00007f309a4a5bca in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
#1 0x00007f309a4ac195 in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2
#2 0x0000000000465453 in CStopwatch::getTime (this=0x7fff0db48c60, delimiterHourMinuteSecondsBy="") at [..] CStopwatch.cpp:86
#3 0x00000000004652a9 in CStopwatch::stop (this=0x7fff0db48c60) at [..] CStopwatch.cpp:51
#4 0x0000000000479a0c in main (argc=33, argv=0x7fff0db499c8) at [..] coherent_ofdm_tsync_mse.cpp:998
问题出现在程序开头创建的class CStopwatch
对象中。秒表在最顶部的 main() 中启动。模拟完成后,调用函数CStopwatch::stop( )
。
秒表的构造函数class:
/*
* Initialize start and stop time on construction
*/
CStopwatch::CStopwatch()
{
this->startTime = std::time_t( 0 );
this->stopTime = std::time_t( 0 );
this->isRunning = false;
}
函数CStopwatch::stop( )
/*
* Stop the timer and return the elapsed time as a string
*/
std::string CStopwatch::stop( )
{
if ( this->isRunning ) {
this->stopTime = std::time( 0 );
}
this->isRunning = false;
return getTime( );
}
函数CStopwatch::getTime()
/*
* Return the elapsed time as a string
*/
std::string CStopwatch::getTime( std::string delimiterHourMinuteSecondsBy )
{
std::ostringstream timeString;
// ...some string init
// time in seconds
double seconds;
if ( this->isRunning ){
// return instantaneous time
seconds = std::difftime(time(0), startTime);
} else {
// return stopped time
seconds = std::difftime(stopTime, startTime); // <-- line where the
// program crashed
}
// ..convert seconds into a string
return timeString.str( );
}
程序开始时CStopwatch::start( )
被调用
/*
* Start the timer, if watch is already running, this is effectively a reset
*/
void CStopwatch::start( )
{
this->startTime = std::time( 0 );
this->isRunning = true;
}
没有进一步的上下文无法判断,但是:
this
可能 null
或损坏
startTime
可能是空引用
stopTime
可能是空引用
我打算建议你在行上设置一个断点并打印出 stopTime
和 startTime
,但你已经 nearly 完成了通过查看核心文件。
看起来好像有什么地方出错了 link 中的函数。可能是您正在针对标准库中的另一组 headers 进行编译 link 去?
可能只是内存相关:
- 如果这是深层嵌套,您可能只是堆栈溢出。
- 如果这是它第一次被调用,也许它正在尝试为库分配内存,加载它,然后 link 它,并且由于达到内存限制而失败
如果此代码路径被多次调用,并且从未在其他地方崩溃,也许是时候 运行 memtest86
一整夜了。
看起来 std::difftime
在第一次访问时被延迟加载;如果某些运行时链接器的内部状态在程序的其他地方被损坏,可能会导致这种情况。
请注意,_dl_runtime_resolve
必须在 std::difftime
调用开始之前完成,因此错误不太可能与您的时间值有关。您可以通过在gdb中打开核心文件轻松验证:
(gdb) frame 2 # this is CStopwatch::getTime
(gdb) print this
(gdb) print *this
etc. etc.
如果 gdb 能够读取和解析地址,并且值看起来正常,那肯定不会在运行时导致 SIGBUS。或者,您的筹码有可能被砸碎了;如果 _dl_fixup
正在准备蹦床跳跃而不是仅仅处理搬迁等;不看代码我们无法确定,但可以检查堆栈本身:
(gdb) print %rsp
(gdb) x/16xb $rsp-16 # print the top 16 bytes of the stack
可以尝试的简单解决方法是设置 LD_BIND_NOW
环境变量并在启动时强制进行符号解析。但这只是隐藏了问题,因为 一些 内存仍在某处 损坏,而我们只是隐藏了症状。
至于正确解决问题 - 即使短期运行没有出现错误,也可能发生一些内存损坏但没有症状。在 valgrind 下尝试 运行 更短的模拟并修复所有警告和错误,除非您确定它们是良性的。
程序可能在 Linux 收到 SIGBUS
的原因只有几个。 this question.
的答案中列出了几个
查看 /var/log/messages
崩溃时间前后,您可能会发现存在磁盘故障或其他导致内核不愉快的原因。
另一种(不太可能)的可能性是有人更新了 libstdc++.so.6
而 您的程序是 运行,并且这样做不正确(通过覆盖现有文件,而不是删除它并在其位置创建新文件)。
今天,我不得不惊恐地意识到我的 C++ 模拟程序在 运行 后崩溃了 12 天,就在它结束前几行,除了一个(截断的)核心转储,我什么都没有。
用 gdb 分析核心转储显示,
Program terminated with signal SIGBUS, Bus error.
并且崩溃发生在我的代码的以下行:
seconds = std::difftime(stopTime, startTime); // seconds is of type double
变量 stopTime
和 startTime
的类型为 std::time_t
,我能够在崩溃时从核心转储中提取它们的值:
startTime: 1426863332
stopTime: 1427977226
difftime 调用上方的堆栈跟踪如下所示:
#0 0x.. in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
#1 0x.. in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2
我写了一个小程序重现错误,但没有成功。仅使用上述值调用 std::difftime(stopTime, startTime)
不会导致 SIGBUS 崩溃。当然,我不希望这种情况再次发生。我之前曾多次成功执行过相同的程序(尽管使用不同的参数),执行时间相当。是什么导致了这个问题,以后我该如何预防?
这是一些额外的系统信息。
GCC: (SUSE Linux) 4.8.1 20130909 [gcc-4_8-branch revision 202388]
Linux Kernel: 3.11.10-25-desktop, x86_64
C++ standard library: 6.0.18
编辑
这里有更多的上下文。首先,完整的堆栈跟踪(省略号 [..]
我的):
#0 0x00007f309a4a5bca in _dl_fixup () from /lib64/ld-linux-x86-64.so.2
#1 0x00007f309a4ac195 in _dl_runtime_resolve () from /lib64/ld-linux-x86-64.so.2
#2 0x0000000000465453 in CStopwatch::getTime (this=0x7fff0db48c60, delimiterHourMinuteSecondsBy="") at [..] CStopwatch.cpp:86
#3 0x00000000004652a9 in CStopwatch::stop (this=0x7fff0db48c60) at [..] CStopwatch.cpp:51
#4 0x0000000000479a0c in main (argc=33, argv=0x7fff0db499c8) at [..] coherent_ofdm_tsync_mse.cpp:998
问题出现在程序开头创建的class CStopwatch
对象中。秒表在最顶部的 main() 中启动。模拟完成后,调用函数CStopwatch::stop( )
。
秒表的构造函数class:
/*
* Initialize start and stop time on construction
*/
CStopwatch::CStopwatch()
{
this->startTime = std::time_t( 0 );
this->stopTime = std::time_t( 0 );
this->isRunning = false;
}
函数CStopwatch::stop( )
/*
* Stop the timer and return the elapsed time as a string
*/
std::string CStopwatch::stop( )
{
if ( this->isRunning ) {
this->stopTime = std::time( 0 );
}
this->isRunning = false;
return getTime( );
}
函数CStopwatch::getTime()
/*
* Return the elapsed time as a string
*/
std::string CStopwatch::getTime( std::string delimiterHourMinuteSecondsBy )
{
std::ostringstream timeString;
// ...some string init
// time in seconds
double seconds;
if ( this->isRunning ){
// return instantaneous time
seconds = std::difftime(time(0), startTime);
} else {
// return stopped time
seconds = std::difftime(stopTime, startTime); // <-- line where the
// program crashed
}
// ..convert seconds into a string
return timeString.str( );
}
程序开始时CStopwatch::start( )
被调用
/*
* Start the timer, if watch is already running, this is effectively a reset
*/
void CStopwatch::start( )
{
this->startTime = std::time( 0 );
this->isRunning = true;
}
没有进一步的上下文无法判断,但是:
this
可能null
或损坏startTime
可能是空引用stopTime
可能是空引用
我打算建议你在行上设置一个断点并打印出 stopTime
和 startTime
,但你已经 nearly 完成了通过查看核心文件。
看起来好像有什么地方出错了 link 中的函数。可能是您正在针对标准库中的另一组 headers 进行编译 link 去?
可能只是内存相关:
- 如果这是深层嵌套,您可能只是堆栈溢出。
- 如果这是它第一次被调用,也许它正在尝试为库分配内存,加载它,然后 link 它,并且由于达到内存限制而失败
如果此代码路径被多次调用,并且从未在其他地方崩溃,也许是时候 运行 memtest86
一整夜了。
看起来 std::difftime
在第一次访问时被延迟加载;如果某些运行时链接器的内部状态在程序的其他地方被损坏,可能会导致这种情况。
请注意,_dl_runtime_resolve
必须在 std::difftime
调用开始之前完成,因此错误不太可能与您的时间值有关。您可以通过在gdb中打开核心文件轻松验证:
(gdb) frame 2 # this is CStopwatch::getTime
(gdb) print this
(gdb) print *this
etc. etc.
如果 gdb 能够读取和解析地址,并且值看起来正常,那肯定不会在运行时导致 SIGBUS。或者,您的筹码有可能被砸碎了;如果 _dl_fixup
正在准备蹦床跳跃而不是仅仅处理搬迁等;不看代码我们无法确定,但可以检查堆栈本身:
(gdb) print %rsp
(gdb) x/16xb $rsp-16 # print the top 16 bytes of the stack
可以尝试的简单解决方法是设置 LD_BIND_NOW
环境变量并在启动时强制进行符号解析。但这只是隐藏了问题,因为 一些 内存仍在某处 损坏,而我们只是隐藏了症状。
至于正确解决问题 - 即使短期运行没有出现错误,也可能发生一些内存损坏但没有症状。在 valgrind 下尝试 运行 更短的模拟并修复所有警告和错误,除非您确定它们是良性的。
程序可能在 Linux 收到 SIGBUS
的原因只有几个。 this question.
查看 /var/log/messages
崩溃时间前后,您可能会发现存在磁盘故障或其他导致内核不愉快的原因。
另一种(不太可能)的可能性是有人更新了 libstdc++.so.6
而 您的程序是 运行,并且这样做不正确(通过覆盖现有文件,而不是删除它并在其位置创建新文件)。