c++浮动并valgrind奇怪的行为
c++ floats and valgrind strange behaviour
我有valgrind 3.6.0,我到处找都找不到。
问题是,当我在使用valgrind时试图访问一个浮点数时,我会得到一个segfault,但当我按原样运行程序时,如果没有valgrind,一切都会按预期进行。
这是一段代码:
class MyClass {
public:
void end() {
float f;
f = 1.23;
std::stringstream ss;
ss << f;
std::cout << ss.str();
}
};
extern "C" void clean_exit_on_sig(int sig) {
//Code logging the error
mc->end();
exit(1);
}
MyClass *mc;
int main(int argc, char *argv[]) {
signal(SIGINT , clean_exit_on_sig);
signal(SIGABRT , clean_exit_on_sig);
signal(SIGILL , clean_exit_on_sig);
signal(SIGFPE , clean_exit_on_sig);
signal(SIGSEGV, clean_exit_on_sig);
signal(SIGTERM , clean_exit_on_sig);
mc = new MyClass();
while(true) {
// Main program loop
}
}
当我按下Control+C时,程序会正确地捕捉到信号,一切都很好,但当我使用valgrind运行程序时,当我试图执行这个命令ss << f; // (Inside MyClass)
时,会抛出一个segfault:-/
我也试过这个:
std::string stm = boost::lexical_cast<std::string>(f);
但当boost也加速浮点数时,我一直收到segfault信号。
这是当我使用boost:获得segfault时的回溯
./a.out(_Z17clean_exit_on_sigi+0x1c)[0x420e72]
/lib64/libc.so.6(+0x32920)[0x593a920]
/usr/lib64/libstdc++.so.6(+0x7eb29)[0x51e6b29]
/usr/lib64/libstdc++.so.6(_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE15_M_insert_floatIdEES3_S3_RSt8ios_baseccT_+0xd3)[0x51e8f43]
/usr/lib64/libstdc++.so.6(_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE6do_putES3_RSt8ios_basecd+0x19)[0x51e9269]
/usr/lib64/libstdc++.so.6(_ZNSo9_M_insertIdEERSoT_+0x9f)[0x51fc87f]
./a.out(_ZN5boost6detail26lexical_stream_limited_srcIcSt15basic_streambufIcSt11char_traitsIcEES4_E9lcast_putIfEEbRKT_+0x8f)[0x42c251]
./a.out(_ZN5boost6detail26lexical_stream_limited_srcIcSt15basic_streambufIcSt11char_traitsIcEES4_ElsEf+0x24)[0x42a150]
./a.out(_ZN5boost6detail12lexical_castISsfLb0EcEET_NS_11call_traitsIT0_E10param_typeEPT2_m+0x75)[0x428349]
./a.out(_ZN5boost12lexical_castISsfEET_RKT0_+0x3c)[0x426fbb]
./a.out(This line of code corresponds to the line where boost tries to do the conversion)
这是默认的字符串流转换:
./a.out(_Z17clean_exit_on_sigi+0x1c)[0x41deaa]
/lib64/libc.so.6(+0x32920)[0x593a920]
/usr/lib64/libstdc++.so.6(+0x7eb29)[0x51e6b29]
/usr/lib64/libstdc++.so.6(_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE15_M_insert_floatIdEES3_S3_RSt8ios_baseccT_+0xd3)[0x51e8f43]
/usr/lib64/libstdc++.so.6(_ZNKSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE6do_putES3_RSt8ios_basecd+0x19)[0x51e9269]
/usr/lib64/libstdc++.so.6(_ZNSo9_M_insertIdEERSoT_+0x9f)[0x51fc87f]
./a.out(This line of code corresponds to the line where I try to do the conversion)
a.out
是我的程序,我以这种方式运行valgrind:valgrind --tool=memcheck ./a.out
另一件奇怪的事情是,当我在程序运行良好的情况下调用mc->end();
时(收到任何信号,Object刚刚完成他的工作),我不会以任何方式得到segfault(按原样和valgrind)。
请不要告诉我"不要用Control+C之类的东西关闭你的程序…"这段代码用于记录程序可能出现的任何错误,而不会在发生segfault时丢失数据,也不会因为死锁或其他原因而导致数据丢失。
编辑:可能是一个valgrind错误(我不知道,在谷歌上搜索了一下,但什么都没有找到,不要杀了我),任何变通方法都会被接受。
EDIT2:刚刚意识到boost也调用了ostream(这里比使用vim://更清楚),将尝试sprintf浮点转换。
EDIT3:尝试了此sprintf(fl, "%.1g", f);
,但仍然崩溃,回溯:
./a.out(_Z17clean_exit_on_sigi+0x40)[0x41df24]
/lib64/libc.so.6(+0x32920)[0x593a920]
/lib64/libc.so.6(sprintf+0x56)[0x5956be6]
./a.out(Line where sprintf is)
好吧,经过几个小时的阅读和研究,我发现了问题,我要回答我自己的问题,因为没有人回答,只有@Kerrek SB[的评论https://stackoverflow.com/users/596781/kerrek-sb]但我不能接受任何评论。(谢谢)
这就像在信号处理程序中一样简单,您只能安全地调用一组函数:http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
如果您调用一些非异步安全函数,它们可以工作,但并不总是如此。
如果你想在信号处理程序中调用非异步安全函数,你可以这样做:
- 创建2个管道。
int pip1[2]; int pip2[2]; pipe(pip1); pipe(pip2);
- 创建一个新线程,并使线程等待从第1个管道
read(pip1[0], msg, 1);
接收一些数据 - 调用信号处理程序时,使用
write
异步安全函数写入第1个管道write(pip1[1], "0", 1);
- 然后用
read(pip2[0], msg, 1);
使信号等待第二个管道 - 线程将唤醒并完成他必须完成的所有工作(在这种情况下,将数据保存到数据库),然后使线程将数据写入第二个管道
write(pip2[1], "0", 1);
- 现在,主线程将唤醒并以
_Exit(1)
或其他内容结束
信息:
我使用2个管道,因为如果我向一个管道写入,然后我读取它,第二个线程可能永远不会醒来,因为主线程已经读取了刚刚写入的数据。我使用一个辅助管道来阻塞主线程,因为我不希望它在第二个线程保存数据时退出。
请记住,信号处理程序可能是在修改共享资源时调用的,如果您的第二个线程访问了该资源,则可能会遇到第二个segfault,因此在使用第二个螺纹访问共享资源时要小心(全局变量或其他)。
如果您正在使用valgrind进行测试,并且不希望在接收到信号时接收到"错误"内存泄漏,则可以在退出pthread_join(2ndthread, NULL)
和exit(1)
而不是_Exit(1)
之前执行此操作。这些都是非异步安全功能,但至少你可以测试内存泄漏,并用信号关闭应用程序,而不会收到"错误"内存泄漏。
希望这能帮助到别人。再次感谢@Kerrek SB.
调试器和其他东西有时会向进程抛出通常不会得到的信号。例如,我不得不更改一个使用recv在gdb下工作的函数。检查你的信号是什么,并在尝试使用它之前验证mc不是null。看看这是否会让你更接近答案。
我认为,也许你使用new(或者其他东西)可能会导致valgrind在初始化mc之前发送一个信号,该信号被你的处理程序捕获。
很明显,您没有粘贴实际的代码,因为您在没有公开end()函数的情况下使用了"class",这意味着它不应该编译。
- valgrind-hellgrind与泄漏检查的结果不同
- 额外分配valgrind
- 如何修复valgrind启动时的致命错误(与libc6-dbg和libc6-dbg:i386连接)
- Valgrind:可以处理更多可能丢失的字节吗?
- 如何在程序执行时查看Valgrind Massif输出(或其他堆分析器)?
- C++/Qt Valgrind 未初始化的字节
- 在 valgrind c++ 上读取大小 8 无效
- 了解 Linux 虚拟内存:valgrind 的 massif 输出显示了有和没有 --pages-as-heap 的主要差异
- Valgrind 在 std::string::swap 中报告 SIGILL
- Valgrind 大小为 8 且地址 0x5b7e520 的读取无效,在大小为 16 的块内为 0 字节 free'd
- 重载 new 和 delete 会导致 valgrind 错误
- Valgrind 在 QThread::start() 上报告内存泄漏
- 我可以让 valgrind 告诉我 _哪个_值未初始化吗?
- 使用 valgrind 检查我的链表暗示中的内存泄漏,让我"肯定丢失:1 个块中有 40 个字节"
- Valgrind 在 std::make_unique 中显示内存泄漏
- 为什么 valgrind 报告两个内存分配,而我的代码只请求一个?
- 如何将Valgrind与Qt Creator一起使用来调试远程应用程序?
- 使用Valgrind时内存泄漏消失
- valgrind:当试图调试在Docker容器内运行的C++程序时,堆块lo/hi大小不匹配
- Valgrind 声称内存释放中的自由空间太多