Как выполнить диагностику ошибок повреждения памяти, возникающих в COM-DLL после ее переноса с Delphi 2007 на Delphi 2009? - PullRequest
3 голосов
/ 06 января 2009

Я только что портировал несколько наших собственных COM-надстроек Outlook с Delphi 2007 на Delphi 2009 и теперь у меня возникают некоторые действительно странные ошибки (прежде чем вы спросите: ни одна из которых, кажется, не имеет никакого очевидного отношения к обработке строк) Например, модальные диалоги, которые вешают Outlook, когда кто-то пытается вызвать их во второй раз (первый раз, когда все выглядит нормально), но только тогда, когда они вызываются из одного определенного обработчика событий, а не когда делают то же самое где-то еще. Когда я прослеживаю ошибку до определенной строки кода и закомментирую эту строку или заменю ее другим кодом с тем же эффектом (например, путем копирования кода, который в противном случае был бы вызван через функцию непосредственно на вызывающий сайт), появится ошибка уйти - обычно только для того, чтобы повторить пару (одинаково неприметных) заявлений позже.

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

Излишне говорить, что у нас не было ни одной из этих проблем при компиляции этих надстроек в Delphi 2007.

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

Более того, я даже точно не знаю, является ли это проблемой с моим собственным кодом для начала (на самом деле я склонен сомневаться в этом в этом случае). Мы широко используем ряд сторонних библиотек (например, JCL, ADX, Redemption). В частности, ADX все еще маркирует свою бета-версию поддержки Delphi 2009.

Я также пытался использовать FastDMM FullDebugMode и действительно обнаружил ряд ошибок в ADX (например, блоки, которые были изменены после освобождения), но все это также происходит, когда я компилирую с Delphi 2007, поэтому он не все же кажется обязательным, что в конечном итоге это является причиной наблюдаемой регрессии.

Итак, как мне справиться с этим? - или еще лучше: где я могу найти хорошие ресурсы для изучения того, как справиться с этим? например учебные пособия по использованию представления CPU или эффективной интерпретации и действию на основе отчетов, выпущенных FastMM? Это правильные инструменты на всех? Куда еще мне обратиться?

Добавление:
Какие типы кода мне следует с подозрением относиться в этом контексте? Какой код даже может вызвать такой хаос в памяти? Единственные места, где я могу думать о том, где мой код выполняет что-либо удаленно приближающееся к явным манипуляциям с памятью, - это резервирование некоторого буферного пространства при подготовке вызова WinAPI. Также имейте в виду, что весь мой код идентичен между версиями Delphi 2007 и Delphi 2009, и в версии Delphi 2007 таких проблем нет.

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

Ответы [ 4 ]

7 голосов
/ 07 января 2009

Не совсем ответ на вопрос, который был более общим, но вполне вероятно, что решение конкретной проблемы, которая его подтолкнула:

Я на 95% уверен, что уже определил проблему! :)

Вот что я сделал:

  • Я включил RangeChecking и OverflowChecking в компиляторе
  • Я выследил и исправил все проблемы, вызвавшие ERangeError или EIntOverflow исключения
    (был один из каждого)
  • Я снова запустил программу с включенным FastMM и FullDebugMode
  • Я наконец-то смог определить причину проблемы во всех случаях, чтобы вызвать функцию JCL GetWindowCaption

Кажется, что GetWindowCaption, очевидно, еще не был проверен на Unicode-совместимость: он использовал значение, возвращаемое функцией API GetWindowTextLength (которая возвращает количество символов) в качестве ввода для ReallocMem (который ожидает количество байтов), чтобы выделить буфер для GetWindowText (который в Delphi 2009 возвращает буфер WideChars). Boom! Функция выделяла слишком мало памяти для буфера, но GetWindowText просто перезаписал следующую память, повредив нижний колонтитул блока.

Я подал это в систему отслеживания ошибок JCL как item # 4648

Суть, которую я извлек из этого: Всегда исправляйте все обнаруженные ошибки! Включая (казалось бы) некритические, такие как ошибки диапазона и переполнения. Если ничего другого, это сделает отладку намного более предсказуемой.

7 голосов
/ 06 января 2009

Лучшим инструментом для решения проблемы, вероятно, являются точки останова памяти.

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

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

Затем вы перезапускаете процесс, создаете точку останова памяти, установленную для всего, что ссылается на этот адрес, и знакомитесь с жизненным циклом этого куска памяти. Минимизация и рационализация ваших шагов воспроизведения помогает здесь. Это может помочь добавить другие точки останова и включить точку останова памяти только позже в приложении; или используйте функции ведения журналов точек останова D2009 для записи значений памяти / стеков вызовов и т. д. вместо фактического взлома отладчиком.

2 голосов
/ 07 января 2009

Тот факт, что вы обнаруживаете ошибки двойного освобождения в D2007, даже если в этой версии это работает нормально, означает, что вам НЕОБХОДИМО исправить их, потому что вам просто повезло, что версии D2007 не нужно перерабатывать память так же агрессивно, как версия D2009, и ошибки не обнаруживаются из-за «сохранения теней» в памяти.
Я бы использовал FastMM fulldebugmode, чтобы найти плохой код и как можно больше его исправить, а затем следовал бы совету Барри, чтобы решить проблему с использованием памяти.
Чтобы узнать, как использовать функции встроенного отладчика и как записывать информацию с не прерывающихся точек останова, вы можете взглянуть на этот сеанс CodeRage 3: Delphi Debugging for Dummies

0 голосов
/ 06 января 2009

Я бы посмотрел в направлении поддержки полной подкачки страниц, встроенной в систему.

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

Ситуация усложняется, когда потребление памяти увеличивается, но, как я уже сказал, сначала попробуйте полную версию peagheap.

...