[This是冗长的,充满了细节。我的具体问题由下面的黑体字问题介绍。]
摘要
我们在valgrind下运行一些测试套件,遇到了一个对我来说没有多大意义的错误。我正在寻求建议,以便更详细地找出可能出错的地方。
- Valgrind抱怨“大小为8的无效写入”。
- 这个错误在每次运行时都是一致的,但是会随着不相关的代码更改、不同的编译器/stdlib版本等而出现。
- 被写入的地址在堆栈上,据我所知,这是一个非常合理的地址,供我们的代码写入。
- 其对齐方式与写入大小一致。
- 它发生的地方是在标准库的深处。
所有这些都让人觉得真实的的问题似乎出在别处:有些东西被破坏了,导致了以后的混乱。但这是valgrind报告的第一个问题,所以如果在其他地方有内存踩踏,那么valgrind就没有抓住它。我怀疑,要么我错过了一些明显的东西,要么有一个微妙的问题,那些比我更有valgrind专业知识的人可能能够指出我。
一些细节
这里有一些细节和一些具体问题。
这是一个在x64硬件上运行Ubuntu 14.04的Linux机器。
下面是Valgrind在一个相当典型的例子中的抱怨:
==14259== Invalid write of size 8
==14259== at 0x662BBC9: __printf_fp (printf_fp.c:663)
==14259== by 0x6629792: vfprintf (vfprintf.c:1660)
==14259== by 0x664D578: vsnprintf (vsnprintf.c:119)
==14259== by 0x52DCE0F: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259== by 0x52E3263: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_float<double>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, char, double) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259== by 0x52E354F: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, double) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259== by 0x52EEAF4: std::ostream& std::ostream::_M_insert<double>(double) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)
==14259== by 0x694725: CRVinfo::appendValue(std::string const&, double) (CRVinfo.cpp:197)
==14259== by 0x6902DB: CRVdouble::info(CRVinfo&) const (CRVdouble.cpp:103)
==14259== by 0x6913B4: CRVcollection::info(CRVinfo&) const (CRVcollection.cpp:60)
==14259== by 0x6913B4: CRVcollection::info(CRVinfo&) const (CRVcollection.cpp:60)
==14259== by 0x68F87F: CRVvalue::generate() (CRVvalue.cpp:71)
==14259== Address 0xffeffde68 is on thread 1's stack
==14259== in frame #0, created by __printf_fp (printf_fp.c:161)
以“CRV”开头的东西是我们的;它们上面的东西在libstdc++和glibc中。Ubuntu 14.04使用的是glibc的2.19版本--除了实际上它似乎使用的是eglibc 2.19而不是普通的glibc 2.19;您可以找到printf_fp.c here的相关版本。
用--vgdb
运行valgrind,并向gdb请求反汇编声明(与上面链接的源代码一致),即当valgrind停止我们时,我们实际要执行的指令是callq __mpn_lshift
。
涉及“我们的”代码的最上面的堆栈帧看起来像这样:
void CRVinfo::appendValue(const std::string &name, double value){
addIndent();
addElementBegin(name);
std::ostringstream oss;
oss << value;
m_valueTree.append(oss.str());
addElementEnd(name);
}
问题就出在oss << value;
里面。m_valueTree
是std::string
;你可以猜出addIndent
和addElementBegin
做什么;后者使用字符串流来完成,前者没有。(可能不相关的注解:你可能会认为这看起来效率很低,你是对的,但这根本不是性能关键的代码。)
因此,无论如何,我们在callq
指令上的地址0xffeffde 68处获得了大小为8的无效写入。您可能希望callq
写入rsp
所指向的内存,因此它确实如此(我已经验证了此时rsp
等于0xffeffde 68)。但是Valgrind反对这个,我不清楚为什么。
(One一个明显的猜测可能是我们的堆栈溢出了。但是(1)我认为这会发生在一个看起来更圆的地址上,(2)我试图增加堆栈大小,但它并没有使这些valgrind抱怨消失,(3)我希望在溢出堆栈时发生segfault,但这并没有发生,(4)我们在这一点上没有使用太多堆栈;在我能够探测到的最早点,rsp
是0xfff 000598,所以我们在故障点使用了不到10 k的堆栈。
问题:我应该明白Valgrind反对这篇文章的原因吗?如果没有,有什么办法让valgrind告诉我更多它不喜欢它的原因吗?
问题:这里的直接问题是valgrind中的一个错误(尽管可能是由我们代码中的一些早期错误引起的),这是否合理?如果是这样,有没有什么好的方法来追踪或排除这些事情?
问题:这看起来像glibc或libstdc++的任何已知问题吗?(我目前所做的网络搜索还没有发现任何这样的已知问题。
更多有用信息
如果我允许在这个无效的写操作之后继续执行,valgrind就会在这里调用的__mpn_lshift
函数中抱怨一个大小为8的无效的 read。它从相同的地址阅读,在gdb中反汇编表明,这是__mpn_lshift
末尾的retq
指令。
我的堆栈帧似乎都不是很大。Valgrind不会抱怨大堆栈帧,不会询问堆栈是否移动,不会建议增加--max-stacksize
,也不会做任何类似的事情。
在另一台gcc版本稍有不同的机器上,可能还有不同版本的标准库,valgrind再次报告__printf_fp
中大小为8的无效写操作,但在它的不同部分,这次不是在调用指令上。(不幸的是,这是在一个同事的计算机上,因为我们观察到了这一点,所以做了一些修改,使他的版本显示出与我相同的失败,所以我无法给予更多的细节。但我有95%的把握,错误发生在mov
指令上,并且严格地写在当前堆栈帧内。
1条答案
按热度按时间but5z9lq1#
这是一个老问题,但有7个赞成票。
问题:我应该明白瓦尔格林反对这篇文章的原因吗?如果没有,有什么办法让valgrind告诉我更多它不喜欢它的原因吗?
使用vgdb并查看变量的unitialised状态。
具体来说(使用Valgrind 3.21及更高版本),您可以执行以下操作
mc xb &value sizeof(value)
那是
mc -memcheck命令的别名
xb -检查字节
&value -要检查的地址
sizeof(value)-长度,本例中为8个字节
问题:这里的直接问题是valgrind中的错误(尽管可能是由我们代码中的一些早期错误引起的),这是否合理?如果是这样,有没有什么好的方法来追踪或排除这些事情?
不是不可能,但更有可能的是错误出现在用户代码中。
问:这看起来像glibc或libstdc++的任何已知问题吗?(我目前所做的网络搜索还没有发现任何这样的已知问题。
不。它看起来像是代码中未初始化的变量。