2009-06-18 3 views
143

Я отлаживаю (собственное) многопоточное приложение C++ под Visual   Studio   2008. В кажущихся случайным случаями я получаю сообщение «Windows вызвало ошибку ...» с примечанием о том, что это может быть связано с коррупция в куче. Эти ошибки не всегда приводят к краху приложения сразу, хотя это может привести к краху после.Как отлаживать ошибки повреждения кучи?

Большая проблема с этими ошибками заключается в том, что они появляются только после того, как на самом деле произошло повреждение, что делает их очень трудными для отслеживания и отладки, особенно в многопоточном приложении.

  • Что может быть причиной этих ошибок?

  • Как отладить их?

Подсказки, инструменты, методы, просветы ... приветствуются.

ответ

115

Application Verifier в сочетании с Debugging Tools for Windows - удивительная установка. Вы можете получить как часть Windows Driver Kit or the lighter Windows SDK. (Узнал о Application Verifier при исследовании earlier question about a heap corruption issue.) В прошлом я использовал BoundsChecker и Insure ++ (упомянутый в других ответах), хотя я был удивлен, сколько функций было в Application Verifier.

Электрический забор (aka «efence»), dmalloc, valgrind и т. Д. Все это стоит упомянуть, но большинство из них намного легче запускать под * nix, чем Windows. Valgrind смехотворно гибкий: я отлаживал большое серверное программное обеспечение с множеством проблем с кучей, используя его.

Когда все остальное не удается, вы можете предоставить свой собственный глобальный оператор new/delete и malloc/calloc/realloc overloads - как это сделать, будет немного отличаться в зависимости от компилятора и платформы - и это будет немного инвестиции - но он может окупиться в долгосрочной перспективе.Список желательной особенности должен выглядеть знакомым из пооддержки и ElectricFence, и на удивление отличная книга Writing Solid Code:

  • караула значение: позволить немного больше пространства до и после каждого Alloc, соблюдая максимальное требование выравнивания; заполнять магическими номерами (помогает перехваты и переполнения буфера буфера и случайный «дикий» указатель)
  • alloc fill: заполните новые распределения магическим значением, отличным от 0 - Visual C++ уже сделает это для вас в Debug строит (помогает использовать вылов неинициализированного Варса)
  • бесплатно заполнить: заполнить в освобожденной памяти с волшебным не-0 значением, предназначенным для запуска Segfault если это разыменовываются в большинстве случаев (помогает ловить висячие указатели)
  • с задержкой: не возвращайте освобожденную память в кучу какое-то время, сохраняйте ее свободной, но недоступной (помогает поймать более висящие указатели, уловы Непосредственный двойной освобождает)
  • трекинг: будучи в состоянии записать, где распределение было сделано иногда может быть полезным

Обратите внимание, что в нашей локальной доморощенной системе (для встроенной цели) мы держим отслеживая отдельно от большинство других вещей, потому что накладные расходы во время выполнения намного выше.


Если вы заинтересованы в более причин перегружать эти распределения функций/операторов, посмотрите на my answer to "Any reason to overload global operator new and delete?"; бесстыдная самореклама, в нем перечислены другие методы, которые полезны при отслеживании ошибок кучи, а также других применимых инструментов.

+3

Одна крошечная вещь, заслуживающая внимания в Application Verifier: вы должны зарегистрировать символы Application Verifier перед символами символа символа Microsoft в пути поиска символа, если вы используете это ... Я немного искал, чтобы понять, почему! Avrf wasn ' t найти необходимые символы. – leander

+0

Application Verifier был очень полезен, и в сочетании с некоторыми догадками я смог решить проблему! Большое спасибо, и всем остальным, за то, что они подняли полезные пункты. – 2009-06-18 15:29:36

+0

Должен ли использоваться Application Verifier с WinDbg или он должен работать с отладчиком Visual Studio? Я пытался использовать его, но он не вызывает никаких ошибок или, по-видимому, ничего не делает, когда я отлаживаю VS2012. –

8

Что может привести к этим ошибкам?

Выполнение непослушных вещей с памятью, например. писать после окончания буфера или записывать в буфер после того, как он был освобожден обратно в кучу.

Как их отладить?

Используйте инструмент, который добавляет автоматизированные оценки проверки на свой исполняемый файл: т.е. VALGRIND на Unix, или инструмент, как BoundsChecker (Википедия предлагает также Очищают и Застраховать ++) на Windows.

Остерегайтесь того, что это замедлит ваше приложение, поэтому они могут быть непригодными для использования, если ваше приложение в режиме реального времени.

Еще одна возможная вспомогательная помощь/инструмент для отладки может быть компонентом MicroQuill's HeapAgent.

+1

Первым шагом будет восстановление приложения с отладочной средой выполнения (флаг MDD или/MTd). Они выполняют дополнительные проверки в malloc и бесплатно, и часто перестают эффективно сужать место ошибки (ов). –

+0

HeqAgent от MicroQuill: об этом мало написано или слышно, но для кучевого повреждения это должно быть в вашем списке. –

+1

BoundsChecker отлично работает как тест на дым, но даже не думает о запуске под ним программы, пытаясь также запустить эту программу в процессе производства. Замедление может быть где угодно от 60x до 300x, в зависимости от того, какие параметры вы используете, и независимо от того, используете ли вы функцию инструментария компилятора. Отказ от ответственности: Я один из тех парней, которые поддерживают продукт для Micro Focus. –

3

Какие функции распределения вы используете? Недавно я ударил аналогичную ошибку, используя функции выделения стиля Heap *.

Оказалось, что я ошибочно создал кучу с опцией HEAP_NO_SERIALIZE. Это существенно упрощает работу кучи без обеспечения безопасности потоков. Это улучшение производительности при правильном использовании, но никогда не должно использоваться, если вы используете HeapAlloc в многопоточной программе [1]. Я упоминаю об этом только потому, что в вашем сообщении упоминается многопоточное приложение. Если вы используете HEAP_NO_SERIALIZE в любом месте, удалите его и, скорее всего, устраните проблему.

[1] Есть определенные ситуации, когда это является законным, но для этого требуется, чтобы вы выполняли сериализацию вызовов в Heap * и, как правило, это не относится к многопоточным программам.

+0

Да: посмотрите параметры компилятора/сборки приложения и убедитесь, что он создан для привязки к «многопоточной» версии библиотеки времени выполнения C. – ChrisW

+0

@ChrisW для API стиля HeapAlloc это другое. Это фактически параметр, который можно изменить в момент создания кучи, а не время соединения. – JaredPar

+0

Ох. Мне не приходило в голову, что OP может говорить об этой куче, а не о куче в CRT. – ChrisW

1

В дополнение к поиску инструментов, подумайте о том, чтобы найти вероятного виновника. Есть ли какой-либо компонент, который вы используете, возможно, не написанный вами, который, возможно, не был спроектирован и протестирован для работы в многопоточной среде? Или просто тот, который вы не знаете знает работает в такой среде.

Последний раз, когда это случилось со мной, это был родной пакет, который в течение многих лет успешно использовался из пакетных заданий. Но впервые в этой компании он был использован из веб-службы .NET (которая многопоточна).Вот и все - они солгали о том, что код является потокобезопасным.

5

Лучший инструмент, который я нашел полезным и работал каждый раз, это обзор кода (с хорошими кодовыми обозревателями).

Помимо проверки кода, сначала я попробую Page Heap. Страница «Куча» занимает несколько секунд, и, если повезет, это может определить вашу проблему.

Если вам не повезло с страницей Heap, скачайте Debugging Tools for Windows у Microsoft и научитесь использовать WinDbg. Извините, не мог дать вам более конкретную помощь, но отладка многопоточной кучи коррупции - это больше искусство, чем наука. Google для «разлома кучи WinDbg», и вы должны найти много статей по этому вопросу.

31

Вы можете обнаружить множество проблем с повреждением кучи, включив страницу «Куча» для вашего приложения. Для этого вам необходимо использовать gflags.exe, который входит в состав Debugging Tools For Windows

Запустите Gflags.exe и в параметрах файла изображения для вашего исполняемого файла установите флажок «Включить страницу».

Теперь перезапустите exe и присоедините его к отладчику. С включенной функцией «Куча страниц» приложение распадается на отладчик всякий раз, когда происходит куча кучи.

+0

+ 1, Его легкий и лучший способ сузить вопрос! –

+0

Да, но однажды я получаю этот вызов функции в моем дампе callstack (после сбоя коррупции памяти): wow64! Wow64NotifyDebugger, что я могу сделать? Я до сих пор не знаю, что не так в моем приложении – Guillaume07

+0

Просто попробовал gflags для отладки кучи здесь, ОЧЕНЬ полезный инструмент, очень рекомендуется. Оказалось, что я обращался к освобожденной памяти, которая, когда инструментальная с gflags сразу же врывается в отладчик ... Handy! –

8

Один быстрый наконечник, который я получил от Detecting access to freed memory это:

Если вы хотите, чтобы найти ошибку быстро, без проверки каждого о том, что доступ к блоку памяти , вы можете установить указатель памяти недопустимого значения после освобождения блока:

#ifdef _DEBUG // detect the access to freed memory 
#undef free 
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666; 
#endif 
3

Если эти ошибки происходят случайным образом, существует высокая вероятность того, что вы столкнулись с ракетами данных. Пожалуйста, проверьте: изменяете ли вы указатели разделяемой памяти из разных потоков? Intel Thread Checker может помочь обнаружить такие проблемы в многопоточной программе.

4

Вы также можете проверить, не связаны ли вы с динамической или статической библиотекой времени C. Если ваши DLL-файлы связаны с статической библиотекой времени C, то файлы DLL имеют отдельные кучи.

Следовательно, если вы должны были создать объект в одной DLL и попытаться освободить его в другой DLL, вы получите то же сообщение, что видите выше. Эта проблема упоминается в другом стеке   Вопрос о переполнении, Freeing memory allocated in a different DLL.

0

Вы можете использовать VC CRT Heap-Check макросы _CrtSetDbgFlag: _CRTDBG_CHECK_ALWAYS_DF или _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024_DF.

10

Чтобы действительно медленные вещи вниз и выполнить много проверки во время выполнения, попробуйте добавить следующую строку в верхней части вашего main() или эквивалент в Microsoft Visual Studio C++

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF); 
0

я хотел бы добавить свой опыт. В последние несколько дней я решил экземпляр этой ошибки в моем приложении. В моем конкретном случае, ошибки в коде были:

  • Удаление элементов из коллекции STL в то время как итерация над ним (я считаю, что есть отладочные флаги в Visual Studio, чтобы поймать эти вещи, я поймал его во время проверки кода)
  • Это один является более сложным, я разделить его на этапах:
    • с родным C++ потока, перезвоните в управляемый код
    • в управляемой земле, называю Control.Invoke и утилизовать управляемый объект, который оборачивает родной объект, к которому относится обратный вызов.
    • Поскольку объект все еще жив внутри собственного потока (он останется заблокированным в обратном вызове до тех пор, пока не закончится Control.Invoke). Я должен уточнить, что использую boost::thread, поэтому я использую функцию-член как функцию потока.
    • Решение: Используйте Control.BeginInvoke (мой GUI создан с Winforms), чтобы естественный поток мог закончиться до уничтожения объекта (цель обратного вызова точно уведомляет о завершении потока и уничтожении объекта).
0

У меня была аналогичная проблема - и он появился совершенно случайно. Возможно, что-то было повреждено в файлах сборки, но я закончил его исправлением, сначала очистив проект, а затем восстановив его.

Таким образом, в дополнение к другим ответам Дано:

Какие вещи могут вызвать эти ошибки? Что-то поврежденное в файле сборки.

Как их отладить? Очистка проекта и восстановление. Если это исправлено, это, вероятно, проблема.

Смежные вопросы