Повреждение кучи под Win32; как найти? - PullRequest
57 голосов
/ 04 августа 2008

Я работаю над многопоточным C ++ приложением, которое портит кучу. Обычные инструменты для обнаружения этой коррупции кажутся неприменимыми. Старые сборки (18 месяцев) исходного кода демонстрируют то же поведение, что и самый последний выпуск, так что это было давно и просто не было замечено; с другой стороны, исходные дельты не могут быть использованы для определения момента появления ошибки - в хранилище много изменений кода.

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

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

Теперь вот к чему:
Когда он запускается в облегченной среде отладки (скажем, Visual Studio 98 / AKA MSVC6), повреждение кучи достаточно легко воспроизвести - проходит десять или пятнадцать минут, прежде чем что-то ужасно выходит из строя и возникают исключения, например alloc; при работе в сложной среде отладки (Rational Purify). , VS2008/MSVC9 или даже Microsoft Application Verifier) ​​система становится ограниченной по скорости памяти и не дает сбоя (связанная с памятью: ЦП не поднимается выше 50%, индикатор диска не горит, программа работает так быстро, как может, коробка потребляет 1.3G 2 ГБ ОЗУ). Итак, У меня есть выбор между способностью воспроизвести проблему (но не определить причину) или возможностью определить причину или проблему, которую я не могу воспроизвести.

Мои лучшие догадки о том, куда идти дальше:

  1. Получить безумно хрюканную коробку (чтобы заменить текущую коробку разработчика: 2 ГБ ОЗУ в E6550 Core2 Duo); это позволит воспроизвести сбой, вызывающий неправильное поведение при работе в мощной среде отладки; или
  2. Перепишите операторы new и delete, чтобы использовать VirtualAlloc и VirtualProtect, чтобы пометить память как доступную только для чтения, как только это будет сделано. Запустите под MSVC6 и пусть ОС поймает плохого парня, который пишет в освобожденную память. Да, это знак отчаяния: кто, черт возьми, переписывает new и delete ?! Интересно, будет ли это так медленно, как при Purify et al.

И, нет: доставка со встроенным прибором Purify невозможна.

Коллега только что прошел мимо и спросил: «Переполнение стека? Мы сейчас получаем переполнение стека?!?»

А теперь вопрос: Как мне найти повреждителя кучи?


Обновление: балансировка new[] и delete[], кажется, прошла долгий путь к решению проблемы. Вместо 15 минут приложение работает примерно за два часа до сбоя. Еще нет. Есть еще предложения? Сохраняется повреждение кучи.

Обновление: сборка релиза под Visual Studio 2008 выглядит значительно лучше; текущее подозрение основано на реализации STL, которая поставляется с VS98.


  1. Воспроизведите проблему. Dr Watson создаст дамп, который может быть полезен для дальнейшего анализа.

Я приму это к сведению, но я обеспокоен тем, что доктор Ватсон будет сбит с толку только после факта, а не тогда, когда наваливается куча.

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

Понятно, что сейчас, опять же, мало помощи, пока что-то не пойдет не так. Я хочу поймать вандала в действии.

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

Я не очень надеюсь, но отчаянные времена требуют ...

И уверены ли вы, что все компоненты проекта имеют правильные настройки библиотеки времени выполнения (C/C++ tab, категория Генерация кода в настройках проекта VS 6.0)?

Нет, нет, и завтра я проведу пару часов, изучая рабочее пространство (в нем 58 проектов) и проверяя, все ли они компилируются и связываются с соответствующими флагами.


Обновление: это заняло 30 секунд. Выберите все проекты в диалоговом окне Settings, снимите флажок, пока не найдете проекты, которые не имеют правильных настроек (все они имеют правильные настройки).

Ответы [ 15 ]

1 голос
/ 04 августа 2008

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

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

Если вы сможете узнать, что именно МОЖЕТ вызвать эту проблему, через Google и документацию об исключениях, которые вы получаете, возможно, это даст дополнительное понимание того, что искать в коде.

0 голосов
/ 03 октября 2009

Пара предложений. Вы упомянули обширные предупреждения на W4 - я бы посоветовал потратить время на исправление кода для его аккуратной компиляции на уровне предупреждений 4 - это будет в значительной степени предотвращать трудно обнаруживаемые ошибки.

Второй - для параметра / analyse - он действительно генерирует обильные предупреждения. Чтобы использовать этот переключатель в моем собственном проекте, я создал новый файл заголовка, который использовал предупреждение #pragma, чтобы отключить все дополнительные предупреждения, генерируемые / анализировать. Далее в файле я включаю только те предупреждения, которые меня волнуют. Затем используйте параметр компилятора / FI, чтобы этот файл заголовка был включен первым во все ваши модули компиляции. Это должно позволить вам использовать переключатель / analysis во время управления выводом

0 голосов
/ 30 июля 2009

Как вы думаете, это состояние гонки? Несколько потоков разделяют одну кучу? Можете ли вы дать каждому потоку отдельную кучу с помощью HeapCreate, тогда они могут работать быстро с HEAP_NO_SERIALIZE. В противном случае, куча должна быть поточно-ориентированной, если вы используете многопоточную версию системных библиотек.

0 голосов
/ 19 декабря 2008

Немного времени мне пришлось решать аналогичную проблему. Если проблема все еще существует, я предлагаю вам сделать это: Мониторинг всех звонков на новые / удалить и malloc / calloc / realloc / free. Я делаю одну DLL, экспортирующую функцию для регистрации всех вызовов. Эта функция получает параметр для определения источника кода, указателя на выделенную область и типа вызова, сохраняя эту информацию в таблице. Все выделенные / освобожденные пары исключаются. В конце или после того, как вам нужно, вы вызываете другую функцию для создания отчета для оставленных данных. При этом вы можете определить неправильные вызовы (новые / бесплатные или malloc / delete) или пропущенные. Если в вашем коде есть какой-либо случай перезаписи буфера, сохраненная информация может быть неправильной, но каждый тест может обнаружить / обнаружить / включить решение выявленной ошибки. Много прогонов, чтобы помочь выявить ошибки. Удачи.

0 голосов
/ 02 сентября 2008

Хорошее предложение Грэма о кастомном malloc / free. Посмотрите, можете ли вы охарактеризовать некоторую модель коррупции, чтобы дать вам возможность использовать ее.

Например, если он всегда находится в блоке одного и того же размера (скажем, 64 байта), измените пару malloc / free, чтобы всегда выделять 64-байтовые чанки на их собственной странице. Когда вы освобождаете 64-байтовый блок, установите биты защиты памяти на этой странице, чтобы предотвратить чтение и чтение (используя VirtualQuery). Тогда любой, кто попытается получить доступ к этой памяти, сгенерирует исключение, а не повредит кучу.

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

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