Резюме
Мы запускаем некоторые из наших тестовых наборов под valgrind и сталкиваемся с ошибкой, которая не имеет большого смысла для меня. Я ищу совет по выяснению более подробно, что может пойти не так.
Все это пахнет так, как будто настоящая проблема в другом: что-то искажается и в дальнейшем приводит к путанице. Но это первый Отчеты о проблемах в valgrind, поэтому, если в другом месте есть переполнение памяти, то valgrind не сможет его поймать. Я подозреваю, что либо я упускаю что-то очевидное, либо есть тонкая проблема, на которую могут указывать те, кто обладает большим опытом, чем я.
Некоторые детали
Вот некоторые детали и некоторые конкретные вопросы.
Это на Linux-машине с Ubuntu 14.04 на оборудовании x64.
Вот жалоба Вальгринда в одном довольно типичном случае:
==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 использует версию 2.19 glibc — за исключением того, что фактически он использует eglibc 2.19, а не обычный glibc 2.19; вы можете найти соответствующую версию printf_fp.c Вот.
Запуск Вальгринда с --vgdb
и запросить у 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
делать; последний использует для этого поток строк, первый — нет. (Вероятно, нерелевантное примечание: вы можете подумать, что это выглядит неэффективно, и вы были бы правы, но это совсем не критичный для производительности код.)
Итак, в любом случае, мы получаем неверную запись размером 8 по адресу 0xffeffde68, на callq
инструкция. Вы ожидаете callq
записать в память, указанную rsp
и так оно и есть (я проверил, что на данный момент rsp
равно 0xffeffde68) … но valgrind возражает против этого, и мне не понятно почему.
(Одно очевидное предположение может заключаться в том, что мы переполняем наш стек. Но (1) я бы подумал, что это произойдет по адресу, выглядящему более круглым, и (2) я попытался увеличить размер стека, и это не заставил эти жалобы Valgrind уйти, и (3) я бы ожидал, что при переполнении стека произошла ошибка, и (4) мы все равно не использовали много стека на этом этапе, в самом начале был в состоянии исследовать, rsp
0xfff000598, поэтому мы использовали менее 10 Кбайт стека в момент сбоя.)
Вопрос: Это должно быть очевидно для меня какие Вальгринд возражает об этом написать? Если нет, есть ли способ заставить Вальгринда рассказать мне больше о том, почему ему это не нравится?
Вопрос: Возможно ли, что непосредственная проблема здесь — это ошибка в valgrind (хотя, возможно, спровоцированная некоторым более ранним неправильным поведением в нашем коде)? Если так, есть ли хороший способ отследить такие вещи или исключить их?
Вопрос: Похоже ли это на любую известную проблему с glibc или libstdc ++? (Такой поиск в Интернете, который я делал до сих пор, не выявил такой известной проблемы.)
Больше информации, если это полезно
Если я разрешу продолжить выполнение после этой недопустимой записи, то valgrind пожалуется — внутри __mpn_lshift
функция вызывается здесь — недействительным читать размера 8. Он читает с того же адреса и разбирает в GDB, что неудивительно, что это retq
инструкция в конце __mpn_lshift
это виновато.
Ни один из моих стековых фреймов не выглядит ужасно большим. Valgrind не жалуется на большие кадры стека, спрашивает, переместился ли стек, предлагает увеличить --max-stacksize
или что-нибудь в этом роде.
На другом компьютере с немного другой версией gcc и, возможно, другими версиями стандартных библиотек, valgrind снова сообщает о недопустимой записи размером 8 в __printf_fp
но в другой части этого и на этот раз не по инструкции звонка. (К сожалению, это было на компьютере коллеги, и, поскольку мы наблюдали это, были сделаны некоторые изменения, которые сделали его версию такой же ошибочной, как и моя, поэтому я не могу дать более подробную информацию с уверенностью. Но я на 95% уверен, что сбой произошел на mov
инструкция, и писал строго внутри текущего стека кадра.)
Задача ещё не решена.
Других решений пока нет …