Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004

.pdf
Скачиваний:
353
Добавлен:
13.08.2013
Размер:
3.3 Mб
Скачать

ГЛАВА 17 Стандартная отладочная библиотека C и управление памятью

613

 

 

Включив в программу директивы #include и #define и вызвав _CrtSetDbgFlag, вы получите полный доступ ко всем функциям библиотеки DCRT, что поможет кон тролировать использование памяти и получать нужную информацию. Вы можете вызывать эти функции в любой момент. Многие из них приспособлены для ис пользования в утверждениях, так что вы можете свободно «рассыпать» их по сво ему коду для раннего обнаружения проблем с памятью.

Ошибка в DCRT

Если вы следуете инструкциям предыдущего раздела, ваш исходный код будет похож на листинг 17 1. В начале листинга вы видите определение _CRTDBG_MAP_ALLOC и заголовочный файл CRTDBG.H, между которыми включаются все остальные заго ловочные файлы. В самом начале main вызывается функция _CrtSetDbgFlag для на стройки DCRT. Далее я выделяю три блока памяти: один при помощи malloc и два при помощи new, — и все три случая вызывают утечку памяти.

Листинг 17-1. Подключение библиотеки DCRT и механизмов отслеживания утечек памяти

// Эту директиву define нужно указывать до включения

//любых заголовочных файлов. #define _CRTDBG_MAP_ALLOC #include <stdio.h>

#include <stdlib.h> #include <string.h> #include <tchar.h>

//Файл CRTDBG.H включается после всех остальных заголовочных файлов. #include <crtdbg.h>

void main ( void )

{

// Включение всех механизмов проверки кучи.

 

_CrtSetDbgFlag (

_CRTDBG_ALLOC_MEM_DF

|

 

_CRTDBG_CHECK_ALWAYS_DF

|

 

_CRTDBG_DELAY_FREE_MEM_DF

|

 

_CRTDBG_LEAK_CHECK_DF

) ;

// Выделение памяти.

 

TCHAR * pNew = new TCHAR[ 200 ] ;

 

TCHAR * pNew2 = new TCHAR[ 200 ] ;

 

TCHAR * pMemLeak

= (TCHAR*)malloc ( 100 ) ;

 

_tcscpy ( pNew , _T ( "New'd memory..." ) ) ; _tcscpy ( pNew2 , _T ( "More new'd memory..." ) ) ; _tcscpy ( pMemLeak , _T ( "Malloc'd memory..." ) ) ;

}

Так как в листинге 17 1 я включил проверку утечек памяти, то при выполне нии программы вы увидите в окне Output нечто вроде:

614 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

Detected memory leaks!

Dumping objects 7>

NewProblem.cpp(22) : {62} normal block at 0x002F2E58, 100 bytes long. Data: <M a l l o c ' d > 4D 00 61 00 6C 00 6C 00 6F 00 63 00 27 00 64 00 G:\vsnet\Vc7\include\crtdbg.h(692) :

{61} normal block at 0x002F2C88, 400 bytes long.

Data: <M o r e n e w > 4D 00 6F 00 72 00 65 00 20 00 6E 00 65 00 77 00 G:\vsnet\Vc7\include\crtdbg.h(692) :

{60} normal block at 0x002F4DC0, 400 bytes long.

Data: <N e w ' d m e > 4E 00 65 00 77 00 27 00 64 00 20 00 6D 00 65 00 Object dump complete.

Формат вывода информации об утечках памяти очень удобен: двойной щел чок выведенного номера строки вызовет автоматический переход к этой строке в исходном файле. Первая утечка происходит в строке 22 файла NEWPROBLEM.CPP (см. листинг 17 1), и двойной щелчок действительно переносит вас на строку, выполняющую malloc. Однако, дважды щелкнув номер строки, указанной в спис ке второй, вы попадете в файл CRTDBG.H (строка 692). Вы увидите вызов new, но это определенно не мой исходный код. Так как в моей программе несколько вы зовов new, можно сделать вывод, что все вызовы new отображаются как исходящие из файла CRTDBG.H. Легко понять, что это не очень поможет при поиске утечек памяти в большой программе!

Проблема заключается в объявлении new в файле CRTDBG.H:

inline void* __cdecl operator new[](size_t s)

{ return ::operator new[](s, _NORMAL_BLOCK, __FILE__, __LINE__); }

Если вы никогда не сталкивались с синтаксисом размещения (placement syntax) оператора new, представленный фрагмент может поначалу показаться странным. Оператор new — очень специфическая конструкция языка C++, так как он может принимать самые разнообразные параметры. Видно, что во время вызова ::operator new ему передаются три дополнительных параметра. Эта версия new определяется

вфайле CRTDBG.H. Кажется, причина неприятностей неочевидна. Макросы __FILE__ и __LINE__ расширяются при компиляции в имя исходного файла и номер строки

внем. Однако, как вы увидели, они расширяются не в ваши файлы и номера строк. Проблема — в первом слове представленного фрагмента: inline. В заключитель ных компоновках объявление функции inline означает, что компилятору реко мендуется не вызывать функцию, а разместить в месте ее вызова сам код функ ции. Но при этом нужно помнить, что в отладочных компоновках функции inline не расширяются и рассматриваются как действительные функции. Соответствен но макросы __FILE__ и __LINE__ в нашем случае расширяются в CRTDBG.H и 692.

Так как это накладывает на использование DCRT серьезные ограничения, я должен был найти способ сделать так, чтобы все обнаруженные утечки указыва ли на выделение памяти в истинном исходном коде. Сначала я хотел изменить CRTDBG.H, но потом решил, что это не очень здорово: все таки это системный заголовочный файл. Поразмыслив, я в конце концов написал следующий макрос, который нужно включать в прекомпилированный заголовочный файл сразу по сле включения CRTDBG.H. Он просто преобразует любой вызов new в версию new с синтаксисом размещения, принимающую дополнительные параметры.

 

ГЛАВА 17 Стандартная отладочная библиотека C и управление памятью

615

 

 

#ifdef _DEBUG

 

#ifndef

NEW_INLINE_WORKAROUND

 

#define

NEW_INLINE_WORKAROUND new ( _NORMAL_BLOCK ,\

 

 

__FILE__ , __LINE__ )

 

#define

new NEW_INLINE_WORKAROUND

 

#endif

 

 

#endif

// _DEBUG

 

Наверное, внимательные читатели догадались, что в моем макросе NEW_INLINE_WOR7 KAROUND скрыта проблема: если у вас есть класс, определяющий оператор new, мой макрос приведет к путанице объявлений. Скорее всего вам не приходится опре делять оператор new в своих классах, но такие библиотеки, как MFC и STL, это точно делают. Вся хитрость — в его определении только внутри прекомпилированного заголовочного файла, во включении в этот файл только таких библиотек, как MFC и STL, и в использовании улучшенных директив #pragma push_macro/#pragma pop_macro. Точные рекомендации на этот счет см. в разделе «Стандартный вопрос отладки: что включать в прекомпилированные заголовочные файлы?».

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

//Сохранение определения оператора new в стеке макроса. #pragma push_macro ( "new" )

//Отмена определения new нужна для правильного объявления класса. #ifdef new

#undef new #endif

class TestClass

{

public :

//Синтаксис размещения new с прототипом, нужным

//для записи правильной информации об исходном

//файле и номере строки при выделении памяти. #ifdef _DEBUG

// iSize

7

размер выделяемой

памяти.

 

// iBlockType

7

тип блока DCRT.

 

 

// lpszFileName

7

имя исходного файла.

 

// nLine

7

номер строки в исходном файле.

 

static void * operator new ( size_t

nSize

,

 

 

int

iBlockType

,

 

 

char *

lpszFileName ,

 

 

int

nLine

)

{

 

 

 

 

// Любой нужный вам код.

// Действительное выделение памяти при помощи _malloc_dbg

616

ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

 

 

 

// и передача всех параметров для записи места этой операции.

 

return ( _malloc_dbg ( nSize

,

 

iBlockType

,

 

lpszFileName

,

 

nLine

) ) ;

}

 

 

#endif

// _DEBUG

 

} ;

 

 

//Восстановление сохраненного значения

//макроса new (т. е. NEW_INLINE_WORKAROUND). #pragma pop_macro ( "new" )

Своим существованием такое решение обязано новым директивам #pragma push_macro и #pragma pop_macro, которые сохраняют текущее определение макроса во внутреннем стеке компилятора и восстанавливают сохраненное значение со ответственно. Вы должны будете заключать в них любой класс, в котором выпол няется перегрузка оператора new, потому что директивы #pragma не могут быть автоматизированы при помощи макросов. Дополнительный оператор new будет вызываться макросом NEW_INLINE_WORKAROUND для изменения фактического выделе ния памяти. Применять дополнительный оператор new немного неудобно, но так вы хоть получите полные отчеты обо всех утечках памяти. Чтобы увидеть, как все это работает, изучите проект FixedNewProblem на CD.

Стандартный вопрос отладки

Что включать в прекомпилированные заголовочные файлы?

Как я упоминал при обсуждении решения ошибки в DCRT, для получения сообщений об утечках памяти, выделяемой при помощи оператора new, и отображения верной информации о номере строки исходного кода, необ ходимо наличие правильных прекомпилированных заголовочных файлов. К тому же это не только облегчит отладку памяти, но и ускорит компиля цию программ. Прекомпилированный заголовочный файл — это по сути записанное на диск дерево грамматического разбора для файлов, указан ных в файле .H (традиционно называемом STDAFX.H). Поэтому вы компи лируете его только раз, а не при каждой компиляции файла .C/.CPP.

Вот правила создания прекомпилированного заголовочного файла.

1.Включайте в него все заголовочные файлы библиотек CRT/компилятора.

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

3.Включайте в него все заголовочные файлы сторонних фирм, такие как файлы для BUGSLAYERUTIL.DLL.

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

ГЛАВА 17 Стандартная отладочная библиотека C и управление памятью

617

 

 

Полезные функции DCRT

Одна из наиболее полезных функций библиотеки DCRT — _CrtCheckMemory — про сматривает всю выделенную вами память и проверяет, не выполняете ли вы за пись данных вне выделенных блоков и не используете ли вы ранее освобожден ные блоки. Даже одна эта функция оправдывает использование всей библиотеки DCRT. Один из великолепных методов обнаружения проблем с памятью состоит в «разбрасывании» вызовов ASSERT ( _CrtCheckMemory ( ) ) ; по всему коду програм мы. Так вы сможете находить ошибки записи данных вне выделенных блоков памяти максимально близко к месту их возникновения.

Другой набор функций позволяет с легкостью проверять корректность любо го блока памяти. Отладочные функции _CrtIsValidHeapPointer, _CrtIsMemoryBlock и

_CrtIsValidPointer прекрасно подходят для проверки параметров. Эти функции вместе с _CrtCheckMemory обеспечивают великолепные возможности проверки памяти.

Еще один полезный набор функций библиотеки DCRT включает функции изу чения состояния памяти: _CrtMemCheckpoint, _CrtMemDifference и _CrtMemDumpStatis7 tics. Благодаря им вы можете легко выполнять сравнение кучи до и после вызова какой нибудь функции, определяя момент, когда что то начинает работать не так. Скажем, если вы используете стандартную библиотеку в группе, вы можете запи сывать состояние кучи до и после вызовов библиотечных функций для обнару жения утечек памяти и определения объема памяти, необходимого для конкрет ной операции.

Сахарной глазурью на пирожном проверки памяти является возможность уста новки ловушки, благодаря которой вы можете узнавать про каждый вызов функ ций выделения и освобождения памяти. Если ловушка выделения памяти возвра щает TRUE, выделение памяти можно продолжить, если же FALSE —выделение па мяти завершается неудачей. Когда я впервые обнаружил эту функциональность, я сразу же понял, что, приложив небольшие усилия, я получу инструменты тести рования кода в по настоящему сложных граничных условиях, которые иначе было бы очень сложно воспроизвести. Результатом этого является модуль MemStress из состава BUGSLAYERUTIL.DLL. Он дает вам возможность принудительно вызывать неудачи выделения памяти в ваших программах, про что я расскажу ниже.

И, наконец, вишенка на сахарной глазури: библиотека DCRT позволяет уста навливать ловушку для функций записи дампа памяти и перечислять клиентские блоки (выделенную вами память). Вы можете заменить функции дампа памяти, используемые по умолчанию, собственными функциями, знающими о ваших дан ных все. После этого вы сможете получать не таинственный дамп памяти по умол чанию (который не только сложен в понимании, но и менее полезен), а точное содержание блока памяти, отформатированное так, как вам угодно. MFC предо ставляет для этого функцию Dump, но она работает только с классами, унаследо ванными от CObject. Я уверен, что если мы с вами в чем то похожи, вы также не можете смириться с написанием программ только при помощи MFC и хотели бы получить более общие функции создания дампов памяти, охватывающие различ ные типы кода.

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

618 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

можно создать некоторые очень интересные утилиты. Так, в функциях MemDumper Validator из библиотеки BUGSLAYERUTIL.DLL я при перечислении клиентских блоков вызываю ловушки записи дампов, чтобы можно было создавать дампы и выполнять проверку многих типов выделенной памяти за одно действие. Это очень мощное средство, позволяющее реализовать глубокую проверку памяти, а не только проверку записи данных вне выделенного блока. Под глубокой проверкой я по нимаю алгоритм, который знает формат данных блока памяти и проверяет кор ректность всех элементов блока с учетом их формата.

Выбор правильной стандартной отладочной библиотеки C для вашего приложения

Некоторое замешательство по поводу использования библиотек CRT при созда нии программ для Microsoft Windows связано с выбором правильной библиоте ки. Существует шесть версий библиотеки CRT, подразделяющихся на две основ ных категории: отладочные (DCRT) и заключительные (CRT). В каждой категории имеется однопоточная статическая библиотека, многопоточная статическая биб лиотека и многопоточная DLL.

При работе со статическими версиями библиотек CRT библиотечные функции компонуются прямо в вашу программу; именно эти версии используются по умол чанию для приложений, создаваемых без помощи мастеров MFC. Преимущество этого подхода в том, что вам не придется поставлять вместе со своей програм мой динамическую библиотеку CRT, а недостаток — в огромном увеличении объема двоичных файлов и рабочего набора. Названия двух вариантов статической биб лиотеки CRT — однопоточной и многопоточной — говорят сами за себя. Если вы создаете DLL и хотите задействовать статическую библиотеку CRT, вам следует выполнять компоновку только с ее многопоточной версией, иначе многопоточ ные приложения не смогут работать с вашей DLL, так как однопоточные стати ческие библиотеки CRT небезопасны с точки зрения потоков.

DLL версии библиотек CRT — MSVCRT(D).DLL — позволяют импортировать биб лиотечные функции CRT. Благодаря этим DLL вы можете уменьшить размер сво их двоичных файлов, а значит, и рабочий набор программы. Поскольку другие приложения будут загружать одни и те же DLL, ОС сможет предоставить несколь ким процессам совместный доступ к страницам кода DLL, и вся система станет работать быстрее. Однако этот вариант имеет и недостаток: вполне возможно, что вам придется распространять еще одну DLL вместе со своей программой.

Чрезвычайно важно, чтобы вы выбрали какую то одну версию библиотеки CRT для всех двоичных файлов, загружаемых в адресное пространство своей основ ной программы. Если некоторые ваши DLL будут обращаться к статической биб лиотеке CRT, а другие — к динамической, вы не только израсходуете дополнитель ное адресное пространство из за дублирования кода, но и создадите плодород ную почву для одной из самых коварных ошибок памяти, на отладку которой могут потребоваться месяцы. При выделении памяти в куче из одной DLL и освобожде нии этой памяти во второй DLL, использующей другую версию библиотеки CRT, ваша программа сможет с легкостью потерпеть крах, потому что освобождающая память DLL не будет знать, откуда взялась выделенная память. Не относитесь к куче

ГЛАВА 17 Стандартная отладочная библиотека C и управление памятью

619

 

 

с пренебрежением: одновременное выполнение разных версий библиотеки CRT влечет за собой различную обработку памяти куч.

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

Для работы с динамическими версиями библиотек CRT я создал библиотеку BUGSLAYERUTIL.DLL. Код расширений MemDumperValidator и MemStress, про ко торые я рассказываю в этой главе, также хранится в BUGSLAYERUTIL.DLL. Эти модули расширения тоже ожидают, что вы будете работать с их DLL версиями. Однако, если вы захотите использовать их в своем приложении не в виде DLL, вы можете отобрать исходные файлы MEMDUMPERVALIDATOR.CPP, MEMDUMPERVALIDATOR.H, MEMSTRESS.CPP, MEMSTRESSCONSTANTS.H и MEMSTRESS.H, изменить указанный метод компоновки функций и включить их в свое приложение.

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

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

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

Использование MemDumperValidator

Расширение MemDumperValidator здорово упрощает отладку памяти. Библиотека DCRT по умолчанию сообщает об утечках памяти и записи данных вне выделен ных блоков. Оба этих сообщения могут пригодиться, но, когда они выглядят сле дующим образом, тип «вытекшей» памяти определить очень сложно:

Detected memory leaks

Dumping objects 7>

TestProc.cpp(104) : {596} normal block at 0x008CD5B0,

24 bytes long.

Data: < k w k > 90 6B 8C 00 B0 DD 8C 00 00 00 80 77 90 6B 8C 00

Object dump complete.

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

620 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

в своих отчетах об утечках памяти, поддерживая к тому же ряд дополнительных методов проверки памяти. А чем больше информации вы будете иметь при отладке, тем быстрее вы ее выполните.

MemDumperValidator использует идентификаторы блоков памяти библиотеки DCRT, позволяющие связать тип блока с набором функций, которым известно его содержание. Всем блокам, выделяемым библиотекой DCRT, присваиваются иден тификаторы (табл. 17 3). Тип блока передается как параметр в функци выделения памяти библиотеки DCRT: _nh_malloc_dbg (new), _malloc_dbg (malloc), _calloc_dbg (calloc)

и _realloc_dbg (realloc).

Табл. 17-3. Идентификаторы блоков памяти

Идентификатор

 

 

блока

Описание

 

_NORMAL_BLOCK

Обычный вызов new, malloc или calloc приводит к созданию нор

 

мального блока. После определения _CRTDBG_MAP_ALLOC вся память в

 

куче выделяется по умолчанию в форме нормальных блоков, кото

 

рые при этом ассоциируются с номером выделившей память стро

 

ки и ее исходным файлом.

 

_CRT_BLOCK

Блоки памяти, выделяемые внутри многих функцияй стандартной

 

библиотеки, отмечаются как блоки CRT, чтобы их можно было об

 

рабатывать отдельно. Это позволяет не проверять их при поиске

 

утечек и других операциях проверки памяти. Ваше приложение

 

никогда не должно выделять, перераспределять или освобождать

 

блок типа CRT.

 

_CLIENT_BLOCK

Чтобы программа следила за специфическим типом памяти, можно

 

вызывать отладочные функции выделения памяти, передавая им

 

специальное значение клиентского блока. Вы можете следить за

 

подтипами клиентских блоков, помещая 16 разрядное значение

 

в старшие 16 битов значения клиентского блока:

 

define CLIENT_BLOCK_VALUE(x) \

 

(_CLIENT_BLOCK|(x<<16))

 

_heap_alloc_dbg ( 10

,

 

CLIENT_BLOCK_VALUE(0xA),

 

__FILE__

,

 

__LINE__

) ;

 

Для записи дампов клиентских блоков памяти можно указать функ

 

цию ловушку, вызвав функцию _CrtSetDumpClient. Ловушка будет вы

 

зываться каждый раз, когда функция библиотеки DCRT захочет сде

 

лать дамп клиентского блока. Кроме того, функция

 

_CrtDoForAllClientObjects позволяет перечислить клиентские блоки,

 

выделенные в данный момент.

 

MFC использует идентификатор клиентских блоков для всех клас

 

сов, унаследованных от CObject. MemDumperValidator использует

 

ловушку записи дампов клиентских блоков.

_FREE_BLOCK

В нормальных условиях вызов функции освобождения памяти при

 

водит к удалению блока памяти из списков отладочной кучи. Одна

 

ко, если через функцию _CrtSetDbgFlag установить флаг

 

_CRTDBG_DELAY_FREE_MEM_DF, память не освобождается, а остается выде

 

ленной и заполняется значениями 0xDD.

ГЛАВА 17

Стандартная отладочная библиотека C и управление памятью

621

 

 

Табл. 17-3. Идентификаторы блоков памяти (продолжение)

 

 

 

 

Идентификатор

 

 

блока

Описание

 

_IGNORE_BLOCK

Если временно отключить функции отслеживания памяти библио

 

теки DCRT, все выделенные в это время блоки памяти будут отме

 

чены как игнорируемые блоки.

 

 

 

 

После того как вы зададите использование расширения MemDumperValidator для класса или типа данных C, библиотека DCRT будет вызывать MemDumperVali dator для создания дампа блока памяти. Это расширение будет анализировать значение блока и вызывать соответствующую функцию записи дампа, если, ко нечно, такая функция будет обнаружена. Проверка блоков памяти выполняется аналогично за исключением того, что библиотека DCRT вызывает соответствую щие функции проверки.

Описать MemDumperValidator просто — сложнее привести его в рабочее со стояние. В листинге 17 2 показан файл MEMDUMPERVALIDATOR.H, выполняющий за вас большую часть инициализации. Включив в свою программу файл BUGSLAYER UTIL.H, вы автоматически включите и MEMDUMPERVALIDATOR.H.

Листинг 17-2. MEMDUMPERVALIDATOR.H

/*——————————————————————————————————————————————————————————————————————

Отладка приложений для Microsoft .NET и Microsoft Windows Copyright © 199772003 John Robbins — All rights reserved.

——————————————————————————————————————————————————————————————————————*/

#ifndef _MEMDUMPERVALIDATOR_H #define _MEMDUMPERVALIDATOR_H

// Нужно включать не MEMDUMPERVALIDATOR.H, а BUGSLAYERUTIL.H. #ifndef _BUGSLAYERUTIL_H

#error "Include BUGSLAYERUTIL.H instead of this file directly!"

#endif

//

_BUGSLAYERUTIL_H

#ifdef __cplusplus

extern "C"

{

#endif

 

// __cplusplus

// Эта библиотека применяется только в отладочных компоновках. #ifdef _DEBUG

////////////////////////////////////////////////////////////////////////

//Объявления typedef для функций записи дампа и проверки памяти

////////////////////////////////////////////////////////////////////////

//Функция записи дампа памяти. Единственный ее параметр — указатель на блок

//памяти. Эта функция может выводить данные блока памяти в любом формате,

//но ради согласованности в ней следует использовать тот же механизм отчетов,

//что применяется в оставшейся части библиотеки DCRT.

typedef void (*PFNMEMDUMPER)(const void *) ;

// Функция проверки памяти. Первый ее параметр — указатель

см. след. стр.

622ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

//на проверяемый блок памяти, а второй — указатель на данные

//о контексте, переданный в функцию ValidateAllBlocks.

typedef void (*PFNMEMVALIDATOR)(const void * , const void *) ;

////////////////////////////////////////////////////////////////////////

//Полезные макросы

////////////////////////////////////////////////////////////////////////

//Макрос установки значения подтипа клиентского блока. Этот макрос

//обеспечивает единственный надежный способ присвоения значения

//полю dwValue в структуре BSMDVINFO, описанной ниже.

#define CLIENT_BLOCK_VALUE(x) (_CLIENT_BLOCK|(x<<16)) // Макрос получения подтипа блока.

#define CLIENT_BLOCK_SUBTYPE(x) ((x >> 16) & 0xFFFF)

////////////////////////////////////////////////////////////////////////

//Объявления, нужные для инициализации функций записи дампа и проверки

//памяти в соответствии со специфическим подтипом клиентского блока

////////////////////////////////////////////////////////////////////////

typedef struct tag_BSMDVINFO

{

//Подтип клиентского блока. Он должен устанавливаться при помощи

//описанного выше макроса CLIENT_BLOCK_VALUE. Чтобы увидеть

//присвоение значения этому полю, см. функцию AddClientDV.

unsigned long

dwValue

;

// Указатель

на функцию записи дампа.

PFNMEMDUMPER

 

pfnDump

;

// Указатель на функцию проверки памяти. PFNMEMVALIDATOR pfnValidate ;

} BSMDVINFO , * LPBSMDVINFO ;

/*——————————————————————————————————————————————————————————————————————

ФУНКЦИЯ: AddClientDV ОПИСАНИЕ:

Добавляет в список функции записи дампа и проверки клиентского блока. Если поле dwValue в структуре BSMDVINFO равно 0, задается следующее значение в списке. Значение dwValue после вызова этой функции всегда должно передаваться функции _malloc_dbg в качестве значения подтипа клиентского блока.

Если значение подтипа устанавливается через CLIENT_BLOCK_VALUE, для передачи значения в _malloc_dbg можно использовать макрос.

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

ПАРАМЕТРЫ:

lpBSMDVINFO 7 указатель на структуру BSMDVINFO ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ:

1 – функции записи дампа и проверки клиентского блока добавлены правильно. 0 – функции записи дампа и проверки клиентского блока не были добавлены.

——————————————————————————————————————————————————————————————————————*/ BUGSUTIL_DLLINTERFACE int __stdcall

AddClientDV (LPBSMDVINFO lpBSMDVInfo);

Соседние файлы в предмете Программирование на C++