Как отладить ошибки повреждения кучи? - PullRequest
160 голосов
/ 18 июня 2009

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

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

  • Какие вещи могут вызвать эти ошибки?

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

Советы, инструменты, методы, просветления ... приветствуются.

Ответы [ 14 ]

125 голосов
/ 18 июня 2009

Application Verifier в сочетании с Средства отладки для Windows - удивительная настройка. Вы можете получить как часть Windows Driver Kit, так и более легкий Windows SDK . (Узнал о Application Verifier при исследовании более раннего вопроса о проблеме повреждения кучи .) В прошлом я также использовал BoundsChecker и Insure ++ (упомянутые в других ответах), хотя я был удивлен, насколько много функциональности было в приложении Verifier.

Electric Fence (он же "efence"), dmalloc , valgrind и так далее, стоит упомянуть, но большинство из них гораздо проще запустить под * nix, чем Окна. Valgrind смехотворно гибок: я отлаживал программное обеспечение для больших серверов со многими проблемами кучи, используя его.

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

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

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


Если вас интересуют другие причины перегрузки этих функций / операторов распределения, взгляните на мой ответ на «Любая причина перегрузить глобальный оператор new и delete?» ; Помимо бесстыдного саморекламы, в нем перечислены другие методы, которые помогают отслеживать ошибки повреждения кучи, а также другие применимые инструменты.


Поскольку я продолжаю находить здесь свой собственный ответ при поиске значений alloc / free / fence, которые использует MS, вот другой ответ, который охватывает значения заполнения Microsoft dbgheap .

35 голосов
/ 18 июня 2009

Вы можете обнаружить много проблем с повреждением кучи, включив Page Heap для своего приложения. Для этого вам нужно использовать gflags.exe, который входит в состав Средства отладки для Windows

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

Теперь перезапустите ваш exe-файл и подключитесь к отладчику. С включенным Page Heap приложение будет работать в режиме отладки при любом повреждении кучи.

13 голосов
/ 06 августа 2009
12 голосов
/ 14 января 2012

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

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
8 голосов
/ 18 июня 2009

Один быстрый совет, который я получил от Обнаружение доступа к освобожденной памяти это:

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

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif
8 голосов
/ 18 июня 2009

Какие вещи могут вызывать эти ошибки?

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

Как мне их отладить?

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

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

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

5 голосов
/ 18 июня 2009

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

Кроме проверки кода, я бы сначала попробовал Page Heap . Page Heap настраивается за несколько секунд, и, если повезет, он может точно определить вашу проблему.

Если вам не повезло с Page Heap, скачайте Средства отладки для Windows от Microsoft и научитесь использовать WinDbg. Извините, что не могу дать вам более конкретной помощи, но отладка многопоточного повреждения кучи - это больше искусство, чем наука. Google для "Повреждение кучи WinDbg", и вы должны найти много статей на эту тему.

4 голосов
/ 04 февраля 2011

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

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

3 голосов
/ 18 июня 2009

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

3 голосов
/ 18 июня 2009

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...