В этой статье я расскажу о программе MemProof фирмы Automated QA. С помощью этой программы можно отслеживать использование памяти в программах написанных на Delphi или C++ Builder. С ее помощью можно выяснить, есть ли утечка памяти (memory leak) в исследуемой программе, а также, сколько памяти и ресурсов используется при выполнении определенных действий. С помощью MemProof можно обнаружить неверные обращения к памяти и вызов функций Windows API с некорректными параметрами. Кроме того, MemProof позволяет отследить возникновение исключительных ситуаций и выполнение запросов к БД через BDE, ODBC и InterBase API.
Под утечкой памяти часто понимается случай, когда выделенная память никогда не освобождается. Однако может оказаться так, что вся выделенная память когда-нибудь освобождается, но при работе программы занятая память постоянно увеличивается, хотя необходимости в этом нет. Типичным примером является ситуация, когда выделяемая программой память освобождается только при выходе из программы (имеется в виду освобождение памяти не операционной системой, а самой программой). Поэтому я предпочитаю другое определение: утечкой памяти называется ситуация, когда программа выделяет память, и не освобождает ее, когда она становится ненужной. Наличие утечек памяти приводит к замедлению работы программы, и может привести к исчерпанию всей виртуальной памяти.
Введение
В этой статье я расскажу о программе MemProof фирмы Automated QA. С помощью этой программы можно отслеживать использование памяти в программах написанных на Delphi или C++ Builder. С ее помощью можно выяснить, есть ли утечка памяти (memory leak) в исследуемой программе, а также, сколько памяти и ресурсов используется при выполнении определенных действий. С помощью MemProof можно обнаружить неверные обращения к памяти и вызов функций Windows API с некорректными параметрами. Кроме того, MemProof позволяет отследить возникновение исключительных ситуаций и выполнение запросов к БД через BDE, ODBC и InterBase API.
Под утечкой памяти часто понимается случай, когда выделенная память никогда не освобождается. Однако может оказаться так, что вся выделенная память когда-нибудь освобождается, но при работе программы занятая память постоянно увеличивается, хотя необходимости в этом нет. Типичным примером является ситуация, когда выделяемая программой память освобождается только при выходе из программы (имеется в виду освобождение памяти не операционной системой, а самой программой).
Поэтому я предпочитаю другое определение: утечкой памяти называется ситуация, когда программа выделяет память, и не освобождает ее, когда она становится ненужной. Наличие утечек памяти приводит к замедлению работы программы, и может привести к исчерпанию всей виртуальной памяти.
Установка и настройка
Скачать MemProof можно с сайта www.automatedqa.com. Последняя версия на момент написания статьи – 0.950. Эта версия распространяется в виде zip-архива без help’а. Для того, чтобы получить справку можно скачать версию 0.936 – это последняя версия, с которой поставляется help. Для установки версии 0.936 необходимо запустить программу установки и ответить на выдаваемые вопросы. Для установки версии 0.950 достаточно распаковать архив в любое удобное для вас место. Перед началом работы рекомендуется внести некоторые изменения в настройки компилятора и линковщика в отлаживаемом проекте. В настройках компилятора рекомендуется отключить оптимизацию (Optimization), включить использование Stack frames, включить генерацию отладочной информации (Debug information) и использование отладочных библиотек (Use Debug DCUs). В настройках линковщика рекомендуется включить Include TD32 debug info.
Даже если проект собран без отладочной информации, все равно можно работать с MemProof. В этом случае можно обнаружить факт утечки, но нельзя выяснить точное место, в котором происходит утечка. К достоинствам MemProof можно отнести тот факт, что для его работы не требуется вносить какие-либо изменения в исходный текст отлаживаемой программы. Так же нет необходимости включать в проект специальные библиотеки. К сожалению, MemProof 0.950 работает не корректно, если запущено несколько его экземпляров одновременно. Вместе с тем, при старте не проверяется, запущен MemProof или нет. Из-за этого приходится самостоятельно следить за тем, чтобы была запущена единственная копия программы. В тоже время MemProof 0.936 позволяет запустить себя только в одном экземпляре. Настройка основных параметров программы MemProof производится в окне Settings, которое открывается при выборе пункта меню Configure / Settings. В следующей таблице перечислены все параметры с кратким описанием.
В окне Active Hooks (вызывается через пункт меню Configure / Active Hooks…) можно выбрать какие события и использование каких ресурсов будет отслеживаться. Основным отслеживаемым ресурсом является память, выделенная с использованием стандартного менеджера памяти Delphi. Для ее контроля предназначен счетчик Live Pointer. При его включении перехватываются вызовы функций GetMem, FreeMem и некоторых других. Здесь же можно настроить слежение за использованием системных ресурсов, возникновением ошибок при обращении к функция Win API, возникновением исключительных ситуаций, обращением к базе данных (через BDE, ODBC или InterBase API). Лучше отключить ненужные ловушки (hooks), т.к. это увеличивает быстродействие и повышает стабильность работы MemProof.
Приложение:
В архиве ForMemProof.zip находится проект, используемый в статье в качестве примера.
В окне Parameters (вызывается через пункт меню Project / Parameters…) задаются параметры командной строки, передаваемые отлаживаемой программе.
В меню Project есть пункт меню Search directories.
При его выборе появляется окно, в котором можно ввести пути к текстам программ и исходным текстам VCL. Заданные пути будут использоваться при отладке открытой в текущий момент программы. Используя пункт меню Configure / Search directories можно задать пути, которые будут использоваться при отладке всех программ. Это имеет смысл, если установлена одна версия Delphi или C++ Builder’а или есть тексты, используемые во всех отлаживаемых проектах.
При отладке программ, состоящих из нескольких исполняемых модулей (exe, dll) возникает необходимость отслеживать использование ресурсов одновременно в нескольких модулях. Предопределить, в каких динамических библиотеках надо отслеживать использование ресурсов, можно выбрав пункт меню Project / Modules configure. В появившемся окне следует отметить нужные библиотеки. При необходимости добавить модуль в список можно, используя кнопку Add. В некоторых случаях, отключение слежения за ресурсами в динамических библиотеках помогает избежать падения исследуемой программы.
Для облегчения использования можно добавить вызов MemProof в меню Tools в среде Delphi или C++ Builder. Для этого необходимо вызывать пункт меню Tools / Configure Tools. В окне Tool Options необходимо добавить новый пункт с помощью кнопки Add. После нажатия кнопки появится окно Tool Properties, в котором следует ввести следующие параметры:
В поле Titile указывается текст пункта меню. Я здесь указываю «&MemProof». Это позволяет вызывать MemProof путем нажатия сочетания клавиш Alt+T, затем M. В Program и Working dir следует ввести полный путь к MemProof.exe и путь к папке, которая будет текущей после его запуска. Текст, введенный в поле Parameters, указывает на то, что при запуске MemProof проект будет сохранен, и при необходимости будет произведена компиляция. После этого будет произведен запуск текущего проекта и ему будут переданы параметры, указанные в окне Run Parameters (вызывается пунктом меню Run / Parameters…).
Обзор интерфейса
В окне программы MemProof можно выделить несколько частей. В левой части находятся кнопки, которые позволяют выбрать, какая информация будет отображаться в правой части окна. При просмотре Resource Counters в правой части выводится количество выделенных ресурсов. При работе исследуемой программы значение счетчиков изменяется в реальном времени (если в окне Settings включен параметр Live update of resource Counters).
При выборе Resources Details правая часть окна разделяется на три части:
- Список не освобожденных блоков памяти и ошибок, произошедших в ходе работы программы.
- Стек вызовов, в месте выделения памяти или в месте возникновения ошибки.
- Текст программы.
Debug Window позволяет отследить возникновение исключительных ситуаций, загрузку и выгрузку динамических библиотек, создание и завершение потоков. В это окно можно вывести отладочную информацию, используя функцию OutputDebugString. При выборе Used DLLs выводится перечень динамических библиотек, используемых отлаживаемым приложением. Нажав на кнопку Results, вы увидите список с отчетами, сгенерированными при работе MemProof. В этих отчетах содержится та же информация что и в ResourceView. Сохраненные здесь результаты могут потеряться при сбоях в работе MemProof. При необходимости сохранить отчет, лучше это сделать с помощью пункта Save to File with Call Stack… в контекстном меню.
Нахождение простых утечек памяти
Самый простой случай утечки – это когда вызывается функция выделяющая память, и никогда не вызывается соответствующая функция, освобождающая память.
Для нахождения таких утечек памяти достаточно выполнить следующие действия:
- Запустить из MemProof отлаживаемую программу.
- В запущенной программе выполнить действия, которые надо протестировать.
- Выйти из программы.
- Проанализировать сгенерированный MemProof’ом отчет.
В отчете будут указаны все места, в которых программа выделила память, которая в дальнейшем не была освобождена. Перед запуском отлаживаемой программы следует убедиться, что слежение за использованием ресурсов включено (пункт меню Run / Resource Tracking).
При поиске таких утечек Live update of resource Counters можно отключить.
Рассмотрим простейший пример утечки памяти.
procedure TForm1.miMemoryLeakClick(Sender: TObject);
var
lst: TStringList;
begin
lst := TStringList.Create;
end;
miMemoryLeakClick – это процедура, которая вызывается при выборе пункта меню miMemoryLeak. Каждый раз при выборе этого пункта создается объект типа TStringList. Созданные таким образом объекты не удаляются до окончания выполнения программы. При выходе из программы операционной системой освобождается вся занятая программой память, в том числе и под объекты созданные в процедуре miMemoryLeakClick.
После выхода из программы будет сформирован следующий отчет:
303 Virtual Memory 022E0000 4096 VirtualAlloc(00000000,4096,4096,64)
0041CFA5 MakeObjectInstance in classes.pas (10990) D:\ForMemProof\Project1.exe
0045D6DB TApplication::CreateHandle in Forms.pas (6314) D:\ForMemProof\Project1.exe
0045D4CB TApplication::Create in Forms.pas (6249) D:\ForMemProof\Project1.exe
00446B8A InitControls in Controls.pas (10924) D:\ForMemProof\Project1.exe
00446CE7 initialization in Controls.pas (10943) D:\ForMemProof\Project1.exe
00403B67 InitUnits in system.pas (10847) D:\ForMemProof\Project1.exe
00403BCE StartExe in system.pas (10918) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
1207 Live Pointer 026746C8 52
00460B1B TForm1::miMemoryLeakClick in Unit1.pas (52) D:\ForMemProof\Project1.exe
0044E526 TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC7B TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
004597D5 TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439D4D TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D91E TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456FF0 TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D4EE TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CF51 StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EBAC TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EBD7 TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE72 TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460ED0 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
В отчете указывается номер блока памяти (или другого ресурса), тип ресурса, адрес блока памяти, название функции, выделившей память или другой ресурс. Если отчет сохранен при помощи пункта меню Save to file…, то содержимое отчета этим и ограничивается. Если же отчет сохранить с помощью пункта меню Save to File with Call Stack…, то в отчет добавляется стек вызовов. Для каждого вызова функции в отчет выводится ее адрес, название, место в исходном тексте (при наличии отладочной информации), имя модуля. Каждому выделенному блоку памяти или ресурсу соответствует уникальный номер. Блок 303 – это память, выделенная в функции MakeObjectInstance.
Блок 1207 – это память, выделенная в процедуре TForm1.miMemoryLeakClick из приведенного примера. Выводится информация только о тех блоках, которые не были освобождены, на момент создания отчета. Чаще всего создаваемые в программе объекты используют динамическую память при своей работе, например, создают другие объекты. Один объект может создавать множество других, и удалять их при своем уничтожении. Если такой объект не будет удален, то не будут удалены и те объекты, которые он должен удалить. В этом случае MemProof выдаст множество строк ссылающихся на одно и то же место в тексте программы. Чтобы определить причину утечки придется анализировать все такие строки. Кроме утечек памяти в программе могут быть утечки системных ресурсов. В следующем примере создается объект «перо», с помощью функции CreatePen.
procedure TForm1.miResourceLeakClick(Sender: TObject);
begin
CreatePen(PS_SOLID, 0, 0);
end;
Каждому вызову CreatePen должен соответствовать вызов функции DeleteObject. В данном примере это не сделано. В отчет будет добавлена информация о каждом вызове функции CreatePen, для которого нет соответствующего вызова DeleteObject.
1075 Pen 92301331 0 CreatePen(0,0,0)
00460B32 TForm1::miResourceLeakClick in Unit1.pas (51) D:\ForMemProof\Project1.exe
0044E52E TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC83 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
004597DD TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439D55 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D926 TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456FF8 TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D4F6 TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CF59 StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EBB4 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EBDF TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE7A TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460EB0 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
Как и в предыдущем случае из отчета можно понять, в каком месте программы происходит утечка. Если вызов функции CreatePen поместить в бесконечный цикл, то счетчик Pen будет постоянно увеличиваться. В Windows XP счетчик остановится, когда общее количество созданных тестируемой программой объектов GDI достигнет 10000. При стандартных настройках именно столько объектов GDI может создать каждая программа в этой операционной системе.
В разделе реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows есть параметры GDIProcessHandleQuota и USERProcessHandleQuota. Они определяют, сколько системных объектов (окна, меню, перья, кисти и др.) может создать одно приложение. Подробнее об этих и других ограничениях можно прочитать на страницах:
- http://msdn2.microsoft.com/en-us/library/ms724291.aspx
- http://msdn2.microsoft.com/en-us/library/ms725486.aspx
- http://msdn2.microsoft.com/en-us/library/ms724485.aspx
- http://support.microsoft.com/kb/326591
- http://support.microsoft.com/kb/327699
Обнаружение утечек памяти без исходных текстов программы
Обнаружить утечку памяти можно даже тогда, когда исходные тексты программы недоступны. Для этого необходимо выполнить следующие действия:
- Запустить MemProof.
- В настройках включить Live update of resource Counters
- Запустить отлаживаемую программу.
- Выполнить действия, которые требуется протестировать.
- Сохранить значение счетчиков (File / Save to File…).
- Повторить несколько раз пункты 4 и 5. Сохранять значение счетчиков следует каждый раз в новом файле.
- Выйти из программы.
- Проанализировать значения счетчиков ресурсов в снимках, сделанных пункте 5.
Четвертый пункт требует некоторых пояснений. Каждый раз при выполнении этого пункта надо выполнять одни и те же действия. Последовательность выполняемых действий должна быть «замкнутой». Например, можно открыть документ, внести в него изменения и закрыть документ. Если при выполнении второго пункта только создавать новый документ, то потребляемая память будет постоянно увеличиваться, и это совершенно нормальное явление. Четвертый и пятый пункт следует выполнить хотя бы два раза. Если значения счетчиков увеличились, тестирование следует продолжить. Если количество используемых ресурсов постоянно увеличивается, то утечки, скорее всего, есть. Для полной уверенности следует выполнить действия из четвертого пункта в бесконечном цикле. Для этого можно воспользоваться любой программой, которая умеет имитировать действия пользователя. Если выполнение бесконечного цикла приводит к исчерпанию виртуальной памяти или системных ресурсов, то утечка в программе есть. В следующем примере создается окно, содержащее информацию о программе.
procedure TForm1.miAboutClick(Sender: TObject);
var
about: TAboutBox;
begin
about := TAboutBox.Create(Application);
about.ShowModal;
end;
При многократном выполнении процедуры TForm1.miAboutClick значения счетчиков будут изменяться следующим образом:
Из таблицы видно, что постоянно увеличивается количество потребляемой памяти и количество используемых системных ресурсов. Не имея исходных текстов программы можно уверенно утверждать, что при открытии окна «О программе» происходят утечки памяти и системных ресурсов.{PAGEBREAK}
Анализ значений счетчиков для нахождения утечек можно производить и при наличии исходных текстов. Выявив действия, при которых происходит утечка, можно приступать к более детальному анализу. Первичное и детальное тестирование может выполняться разными людьми. Проверку наличия утечек памяти можно проводить и при разработке, и при проверке качества программы.
Нахождение сложных утечек памяти
Если каждому вызову функции выделяющей память соответствует вызов функции освобождающей память, это еще не значит, что утечек памяти в программе нет. В последнем примере память, выделенная под объекты типа TAboutBox, не освобождается при закрытии окна. При выходе из программы объекты, созданные в этом примере будут удалены. Однако, при каждом открытии окна «О программе» увеличивается объем занятой памяти. Если открывать окно много раз, то можно исчерпать всю свободную память. Особой разницы с утечкой в процедуре TForm1.miMemoryLeakClick нет. И в том и в другом случае при многократном вызове процедуры программа занимает все больший объем памяти. В обоих случаях память освобождается только при выходе из программы. Однако, во втором случае после выхода из программы, MemProof выдаст пустой отчет.
Для обнаружения таких утечек используется другая технология работы. Существует два способа обнаружения таких утечек:
- включать и отключать слежение за использованием ресурсов во время тестирования;
- в ходе тестирования делать снимки (snapshots) с информацией о количестве выделенных ресурсов.
В первом случае необходимо выполнить следующие действия:
- Запустить MemProof.
- Отключить слежение за ресурсами (Run / Resource Tracking).
- Запустить отлаживаемую программу.
- Непосредственно перед выполнением действий, которые надо протестировать, включить слежение за ресурсами.
- Выполнить действия, которые надо протестировать.
- Отключить слежение за ресурсами.
- Выйти из программы.
- Проанализировать сгенерированный MemProof’ом отчет.
Для того чтобы обнаружить утечку при открытии окна TAboutBox, надо включить слежение за ресурсами перед открытием окна, и выключить после его закрытия. После выхода из программы будет выдан отчет:
591 Live Pointer 02674730 792
0267458F D:\ForMemProof\Project1.exe
00460B6F TForm1::miAboutClick in Unit1.pas (63) D:\ForMemProof\Project1.exe
0044E52E TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC83 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
004597DD TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439D55 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D926 TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456FF8 TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D4F6 TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CF59 StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EBB4 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EBDF TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE7A TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460EB0 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
592 Live Pointer 02674A4C 40
0012FA63 D:\ForMemProof\Project1.exe
00436D88 TControl::Create in Controls.pas (3208) D:\ForMemProof\Project1.exe
0043B4D3 TWinControl::Create in Controls.pas (5367) D:\ForMemProof\Project1.exe
00454E9F TScrollingWinControl::Create in Forms.pas (2065) D:\ForMemProof\Project1.exe
00455AE3 TCustomForm::CreateNew in Forms.pas (2585) D:\ForMemProof\Project1.exe
00455944 TCustomForm::Create in Forms.pas (2556) D:\ForMemProof\Project1.exe
00460B6F TForm1::miAboutClick in Unit1.pas (63) D:\ForMemProof\Project1.exe
0044E52E TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC83 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
004597DD TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439D55 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D926 TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456FF8 TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D4F6 TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CF59 StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EBB4 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EBDF TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE7A TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460EB0 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
594 Live Pointer 026740B4 36
02674A47 D:\ForMemProof\Project1.exe
00436DBC TControl::Create in Controls.pas (3211) D:\ForMemProof\Project1.exe
0043B4D3 TWinControl::Create in Controls.pas (5367) D:\ForMemProof\Project1.exe
00454E9F TScrollingWinControl::Create in Forms.pas (2065) D:\ForMemProof\Project1.exe
00455AE3 TCustomForm::CreateNew in Forms.pas (2585) D:\ForMemProof\Project1.exe
00455944 TCustomForm::Create in Forms.pas (2556) D:\ForMemProof\Project1.exe
00460B6F TForm1::miAboutClick in Unit1.pas (63) D:\ForMemProof\Project1.exe
0044E52E TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC83 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
004597DD TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439D55 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D926 TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456FF8 TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D4F6 TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CF59 StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EBB4 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EBDF TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE7A TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460EB0 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
595 Live Pointer 02674A78 28
0012FA8F D:\ForMemProof\Project1.exe
0043B4F6 TWinControl::Create in Controls.pas (5374) D:\ForMemProof\Project1.exe
00454E9F TScrollingWinControl::Create in Forms.pas (2065) D:\ForMemProof\Project1.exe
00455AE3 TCustomForm::CreateNew in Forms.pas (2585) D:\ForMemProof\Project1.exe
00455944 TCustomForm::Create in Forms.pas (2556) D:\ForMemProof\Project1.exe
00460B6F TForm1::miAboutClick in Unit1.pas (63) D:\ForMemProof\Project1.exe
0044E52E TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC83 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
004597DD TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439D55 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D926 TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456FF8 TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D4F6 TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CF59 StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EBB4 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EBDF TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE7A TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460EB0 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
Это только часть отчета. В отчете содержится информация о блоках памяти, которые были выделены при создании окна TAboutBox (после включения слежения за ресурсами) и не были освобождены при его закрытии (до отключения слежения за ресурсами). Наличие этих блоков в отчете не обязательно свидетельствует об утечке. Может оказаться так, что память, выделенная при включенном слежении за ресурсами и не освобожденная до его выключения, используется при дальнейшей работе программы. Для определения того, какие блоки являются утечками, необходимо анализировать место выделения памяти и ее дальнейшее использование. Кроме информации о выделенных блоках памяти, в отчете есть описание ошибки:
958 Error 026720B8 0 Attempt to free unexisting resource
0041504E TList::SetCapacity in classes.pas (2928) D:\ForMemProof\Project1.exe
00414E14 TList::Grow in classes.pas (2841) D:\ForMemProof\Project1.exe
00414BA3 TList::Add in classes.pas (2754) D:\ForMemProof\Project1.exe
00415245 TThreadList::Add in classes.pas (3115) D:\ForMemProof\Project1.exe
00420100 TCanvas::Create in Graphics.pas (2014) D:\ForMemProof\Project1.exe
00441FBF TGraphicControl::Create in Controls.pas (8555) D:\ForMemProof\Project1.exe
0042C81F TImage::Create in ExtCtrls.pas (1208) D:\ForMemProof\Project1.exe
004191E3 CreateComponent in classes.pas (6096) D:\ForMemProof\Project1.exe
00419418 TReader::ReadComponent in classes.pas (6144) D:\ForMemProof\Project1.exe
004196CE TReader::ReadDataInner in classes.pas (6210) D:\ForMemProof\Project1.exe
00419637 TReader::ReadData in classes.pas (6195) D:\ForMemProof\Project1.exe
0041BEA6 TComponent::ReadState in classes.pas (9987) D:\ForMemProof\Project1.exe
00437271 TControl::ReadState in Controls.pas (3357) D:\ForMemProof\Project1.exe
0043B830 TWinControl::ReadState in Controls.pas (5475) D:\ForMemProof\Project1.exe
004194AB TReader::ReadComponent in classes.pas (6156) D:\ForMemProof\Project1.exe
004196CE TReader::ReadDataInner in classes.pas (6210) D:\ForMemProof\Project1.exe
00419607 TReader::ReadData in classes.pas (6189) D:\ForMemProof\Project1.exe
0041BEA6 TComponent::ReadState in classes.pas (9987) D:\ForMemProof\Project1.exe
00437271 TControl::ReadState in Controls.pas (3357) D:\ForMemProof\Project1.exe
0043B830 TWinControl::ReadState in Controls.pas (5475) D:\ForMemProof\Project1.exe
004560E9 TCustomForm::ReadState in Forms.pas (2723) D:\ForMemProof\Project1.exe
0041A4C9 TReader::ReadRootComponent in classes.pas (6566) D:\ForMemProof\Project1.exe
0041761B TStream::ReadComponent in classes.pas (4975) D:\ForMemProof\Project1.exe
0041488C InternalReadComponentRes in classes.pas (2504) D:\ForMemProof\Project1.exe
00414A60 InitComponent in classes.pas (2561) D:\ForMemProof\Project1.exe
00414AF2 InitInheritedComponent in classes.pas (2573) D:\ForMemProof\Project1.exe
0045598B TCustomForm::Create in Forms.pas (2561) D:\ForMemProof\Project1.exe
00460B6F TForm1::miAboutClick in Unit1.pas (63) D:\ForMemProof\Project1.exe
0044E52E TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC83 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
004597DD TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439D55 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D926 TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456FF8 TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D4F6 TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CF59 StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EBB4 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EBDF TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE7A TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
Это сообщение появляется из-за того, что после включения слежения за ресурсами был освобожден блок памяти, который был выделен до включения слежения. Этот блок не был зарегистрирован MemProof’ом при создании, но было зафиксировано освобождение этого блока. MemProof воспринимает эту как ошибку: освобождение несуществующего блока памяти. На самом деле в этом примере некорректного освобождения памяти нет.
Наличие в отчете «лишних» блоков и «лишних» сообщений об ошибках является издержкой описываемого способа нахождения утечек памяти.
Второй способ позволяет получить отчет без ложных сообщений об ошибках. В этом случае последовательность действий следующая:
- Запустить MemProof и исследуемую программу.
- Выполнить действия, которые необходимо протестировать.
- Выбрать пункт меню View / Snapshot of All Resources.
- Сохранить полученный отчет в файле.
- Повторить несколько раз пункты 2, 3 и 4. Выдаваемый MemProof’ом отчет следует сохранять каждый раз в новом файле.
- Выйти из программы.
- Сравнить содержимое файлов, полученных в пункте 5.
Сравнивать надо соседние снимки, т.е. первый со вторым, второй с третьим, и т.д. При сравнении, из отчетов необходимо исключить блоки с одинаковыми номерами. Если после этого суммарный размер блоков в сравниваемых отчетах совпадут, то при выполнении тестирования утечек не произошло.
Если сравнить отчеты, полученные после открытия и закрытия окна TAboutBox, то окажется, что есть один блок, присутствующий только в первом файле:
2113 Live Pointer 02676A90 48
00414FF2 TList::SetCapacity in classes.pas (2928) D:\ForMemProof\Project1.exe
00414DB8 TList::Grow in classes.pas (2841) D:\ForMemProof\Project1.exe
00414B47 TList::Add in classes.pas (2754) D:\ForMemProof\Project1.exe
004151E9 TThreadList::Add in classes.pas (3115) D:\ForMemProof\Project1.exe
004200A4 TCanvas::Create in Graphics.pas (2014) D:\ForMemProof\Project1.exe
00441F63 TGraphicControl::Create in Controls.pas (8555) D:\ForMemProof\Project1.exe
00430143 TCustomLabel::Create in StdCtrls.pas (1429) D:\ForMemProof\Project1.exe
00419187 CreateComponent in classes.pas (6096) D:\ForMemProof\Project1.exe
004193BC TReader::ReadComponent in classes.pas (6144) D:\ForMemProof\Project1.exe
00419672 TReader::ReadDataInner in classes.pas (6210) D:\ForMemProof\Project1.exe
004195DB TReader::ReadData in classes.pas (6195) D:\ForMemProof\Project1.exe
0041BE4A TComponent::ReadState in classes.pas (9987) D:\ForMemProof\Project1.exe
00437215 TControl::ReadState in Controls.pas (3357) D:\ForMemProof\Project1.exe
0043B7D4 TWinControl::ReadState in Controls.pas (5475) D:\ForMemProof\Project1.exe
0041944F TReader::ReadComponent in classes.pas (6156) D:\ForMemProof\Project1.exe
00419672 TReader::ReadDataInner in classes.pas (6210) D:\ForMemProof\Project1.exe
004195AB TReader::ReadData in classes.pas (6189) D:\ForMemProof\Project1.exe
0041BE4A TComponent::ReadState in classes.pas (9987) D:\ForMemProof\Project1.exe
00437215 TControl::ReadState in Controls.pas (3357) D:\ForMemProof\Project1.exe
0043B7D4 TWinControl::ReadState in Controls.pas (5475) D:\ForMemProof\Project1.exe
0045608D TCustomForm::ReadState in Forms.pas (2723) D:\ForMemProof\Project1.exe
0041A46D TReader::ReadRootComponent in classes.pas (6566) D:\ForMemProof\Project1.exe
004175BF TStream::ReadComponent in classes.pas (4975) D:\ForMemProof\Project1.exe
00414830 InternalReadComponentRes in classes.pas (2504) D:\ForMemProof\Project1.exe
00414A04 InitComponent in classes.pas (2561) D:\ForMemProof\Project1.exe
00414A96 InitInheritedComponent in classes.pas (2573) D:\ForMemProof\Project1.exe
0045592F TCustomForm::Create in Forms.pas (2561) D:\ForMemProof\Project1.exe
00460ADB TForm1::miAboutClick in Unit1.pas (71) D:\ForMemProof\Project1.exe
0044E4D2 TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC27 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
00459781 TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439CF9 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D8CA TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456F9C TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D49A TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CEFD StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EB58 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EB83 TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE1E TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460D88 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
«Лишний» блок в первом файле является «заменой» сообщения об ошибке в предыдущем случае. Отсутствие в отчете ложных сообщений об ошибках облегчает выявление истинных ошибок.
Во втором файле есть множество блоков отсутствующих в первом:
3749 Live Pointer 02676E28 788 TAboutBox
02673EFB D:\ForMemProof\Project1.exe
00460ADB TForm1::miAboutClick in Unit1.pas (71) D:\ForMemProof\Project1.exe
0044E4D2 TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC27 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
00459781 TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439CF9 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D8CA TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456F9C TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D49A TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CEFD StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EB58 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EB83 TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE1E TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460D88 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
3750 Live Pointer 02677140 36 TFont
00002769 D:\ForMemProof\Project1.exe
00436D2C TControl::Create in Controls.pas (3208) D:\ForMemProof\Project1.exe
0043B477 TWinControl::Create in Controls.pas (5367) D:\ForMemProof\Project1.exe
00454E43 TScrollingWinControl::Create in Forms.pas (2065) D:\ForMemProof\Project1.exe
00455A87 TCustomForm::CreateNew in Forms.pas (2585) D:\ForMemProof\Project1.exe
004558E8 TCustomForm::Create in Forms.pas (2556) D:\ForMemProof\Project1.exe
00460ADB TForm1::miAboutClick in Unit1.pas (71) D:\ForMemProof\Project1.exe
0044E4D2 TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC27 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
00459781 TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439CF9 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D8CA TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456F9C TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D49A TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CEFD StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EB58 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EB83 TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE1E TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460D88 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
3752 Live Pointer 02677168 32 TSizeConstraints
0267713B D:\ForMemProof\Project1.exe
00436D60 TControl::Create in Controls.pas (3211) D:\ForMemProof\Project1.exe
0043B477 TWinControl::Create in Controls.pas (5367) D:\ForMemProof\Project1.exe
00454E43 TScrollingWinControl::Create in Forms.pas (2065) D:\ForMemProof\Project1.exe
00455A87 TCustomForm::CreateNew in Forms.pas (2585) D:\ForMemProof\Project1.exe
004558E8 TCustomForm::Create in Forms.pas (2556) D:\ForMemProof\Project1.exe
00460ADB TForm1::miAboutClick in Unit1.pas (71) D:\ForMemProof\Project1.exe
0044E4D2 TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC27 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
00459781 TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439CF9 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D8CA TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456F9C TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D49A TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CEFD StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EB58 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EB83 TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE1E TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460D88 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
3753 Live Pointer 0267718C 24 TBrush
0012FA8F D:\ForMemProof\Project1.exe
0043B49A TWinControl::Create in Controls.pas (5374) D:\ForMemProof\Project1.exe
00454E43 TScrollingWinControl::Create in Forms.pas (2065) D:\ForMemProof\Project1.exe
00455A87 TCustomForm::CreateNew in Forms.pas (2585) D:\ForMemProof\Project1.exe
004558E8 TCustomForm::Create in Forms.pas (2556) D:\ForMemProof\Project1.exe
00460ADB TForm1::miAboutClick in Unit1.pas (71) D:\ForMemProof\Project1.exe
0044E4D2 TMenuItem::Click in Menus.pas (1820) D:\ForMemProof\Project1.exe
0044FC27 TMenu::DispatchCommand in Menus.pas (2540) D:\ForMemProof\Project1.exe
00459781 TCustomForm::WMCommand in Forms.pas (4114) D:\ForMemProof\Project1.exe
00439CF9 TControl::WndProc in Controls.pas (4645) D:\ForMemProof\Project1.exe
0043D8CA TWinControl::WndProc in Controls.pas (6342) D:\ForMemProof\Project1.exe
00456F9C TCustomForm::WndProc in Forms.pas (3097) D:\ForMemProof\Project1.exe
0043D49A TWinControl::MainWndProc in Controls.pas (6237) D:\ForMemProof\Project1.exe
0041CEFD StdWndProc in classes.pas (10965) D:\ForMemProof\Project1.exe
77D38704 GetDC C:\WINDOWS\system32\user32.dll
77D387E6 GetDC C:\WINDOWS\system32\user32.dll
77D389A0 GetWindowLongW C:\WINDOWS\system32\user32.dll
77D3BCC7 DispatchMessageA C:\WINDOWS\system32\user32.dll
0045EB58 TApplication::ProcessMessage in Forms.pas (6872) D:\ForMemProof\Project1.exe
0045EB83 TApplication::HandleMessage in Forms.pas (6891) D:\ForMemProof\Project1.exe
0045EE1E TApplication::Run in Forms.pas (6975) D:\ForMemProof\Project1.exe
00460D88 initialization in Project1.dpr (14) D:\ForMemProof\Project1.exe
7C816D4A RegisterWaitForInputIdle C:\WINDOWS\system32\kernel32.dll
Как и в предыдущем случае приведена только часть отчета. Размер блоков во втором файле больше, чем в первом. Это указывает на то, что после снятия первого снимка и до снятия второго выделена и не освобождена память. Как и в предыдущем случае, не все блоки, присутствующие только во втором файле, являются признаком утечки.
Наличие в отчете информации о множестве не освобожденных блоков памяти осложняет поиск причины утечки. Несколько облегчает задачу информация о том, под какой класс выделен блок памяти. В отчете указано, что блок 3749 выделен под класс TAboutBox. Это говорит о том, что не удален сам объект этого типа, а не просто какая-то память, используемая этим объектом.
Исправлять утечки не следует одним махом. После каждого исправления необходимо проводить проверку использования памяти (анализируя при тестировании значения счетчиков). Если динамика использования памяти не изменилась, то сделанные исправления как минимум бесполезны. При исправлении утечек памяти можно внести ошибки, связанные с некорректным использованием памяти. Это происходит, если после исправления память стала удаляться до окончания использования. Появление таких ошибок может быть более нежелательным, чем наличие в программе утечек памяти.
Отслеживание исключительных ситуаций
Для отслеживания исключительных ситуаций можно включить ловушку (hook) Exception. При возникновении исключительной ситуации в окне Resource Counters появится строка Exceptions. При выборе пункта меню View / Snapshot of Current Resource появиться информация о том, где произошло исключение. Получить, таким образом, информацию об исключении можно только во время работы отлаживаемой программы. После ее завершения список исключений будет пуст. Я сталкивался с тем, что при включении ловушки Exception запущенная из-под MemProof’а программа падает при любом исключении. В таком случае эту ловушку следует выключить. Получить перечень исключений, произошедших во время выполнения программы, можно просматривая Debug Window. В этом окне помимо всего прочего выводится и информация, об исключениях. Если в настройках MemProof’а установлен флажок Trace Call Stack on Exception, то для каждого исключения будет выведен стек вызовов. В Debug Window информация об исключениях записывается и при выключенной ловушке Exception. В следующем примере генерируется исключительная ситуация.
procedure TForm1.miExceptionClick(Sender: TObject);
begin
raise EAbort.Create('Возникла исключительная ситуация');
end;
При выполнении этого кода в Debug Window добавится информация о произошедшей исключительной ситуации:
Exception 0EEDFADE Unknown exception at 7C81EB33
---- Exception stack trace ----
7C81EB33 - RaiseException in (0)
00460BA2 - TForm1::miExceptionClick in Unit1.pas (77)
0044E52B - TMenuItem::Click in Menus.pas (1822)
0044FC80 - TMenu::DispatchCommand in Menus.pas (2541)
004597DA - TCustomForm::WMCommand in Forms.pas (4114)
00439D52 - TControl::WndProc in Controls.pas (4646)
0043D923 - TWinControl::WndProc in Controls.pas (6343)
00456FF5 - TCustomForm::WndProc in Forms.pas (3098)
0043D4F3 - TWinControl::MainWndProc in Controls.pas (6237)
0041CF56 - StdWndProc in classes.pas (10967)
77D38709 - GetDC in (0)
77D387EB - GetDC in (0)
77D389A5 - GetWindowLongW in (0)
77D3BCCC - DispatchMessageA in (0)
0045EBB1 - TApplication::ProcessMessage in Forms.pas (6872)
0045EBDC - TApplication::HandleMessage in Forms.pas (6891)
0045EE77 - TApplication::Run in Forms.pas (6975)
00460E6D - initialization in Project1 (0)
7C816D4F - RegisterWaitForInputIdle in (0)
00000000 - in D:\ForMemProof\Project1.exe
Из этого протокола можно узнать, в каком месте программы произошло исключение.
Заключение
В этой статье описаны не все возможности программы MemProof. Не были подробно рассмотрены тестирование dll-библиотек, обнаружение выходов за границы блока памяти, отслеживание запросов к БД, стресс-тестирование и использование фильтров. Возможности MemProof по отслеживанию запросов к БД сильно ограничены. Использовать его для этой цели стоит только при отсутствии более подходящего инструмента.
К недостаткам MemProof можно отнести нестабильность работы и отсутствие поддержки BDS 2006. Версия 0.95 вышла в 2004 году, и, скорее всего, является последней. Многие возможности MemProof включены в другой продукт компании AutomatedQA – AQTime. Он отличается большей стабильностью работы и большей стоимостью. AQTime, кроме нахождения утечек памяти и системных ресурсов, обладает множеством других возможностей.
При тестировании программы на наличие утечек памяти, как и при любом другом тестировании, можно только обнаружить ошибки, но нельзя убедиться в их отсутствии. Утверждать по результатам тестирования, что утечек в программе нет, нельзя. Можно только утверждать, что утечек не обнаружено. Это не значит, что тестирование проводить бессмысленно. Если в программе не искать утечки, то их количество, скорее всего, будет только увеличиваться. Поиск и устранение утечек памяти – это не одноразовое мероприятие, а непрерывный процесс, который следует вести с самого начала разработки программы.