Отладка приложений

         

Реализация MemDumperValidator


Реализация функций расширения MemDumperValidator не очень сложна. Первая неожиданная проблема, с которой мне пришлось столкнуться, состояла в том, что DCRT-библиотека не документирует способ, с помощью которого hook-функции получают значение блока памяти. Этим функциям передается только указатель на данные пользователя, а не весь блок памяти, который распределяет DCRT-библиотека. К счастью, наличие исходного кода DCRT-библиотеки дало возможность точно видеть, как библиотека распределяет блоки памяти. Все блоки памяти распределяются в виде структуры _GrtMemBiockHeader, которая определена в файле DBGINT.H. Кроме того, в файле DBGINT.H определены макросы, обеспечивающие доступ к полям структуры _GrtMemBiockHeader по указателю данных пользователя и, наоборот, доступ к данным пользователя по указателю этой структуры. Чтобы иметь возможность получать информацию заголовков, я скопировал структуру _CrtMemBiockHeader и макрос доступа в файл заголовков CRTDBG_INTERNALS.H (показанный в листинге 15-3). Полагаться на копию определения структуры, когда это определение может изменяться — не очень хорошая практика, но в данном случае это работает, потому что структура _crtMemBiockHeader DCRT-библиотеки не изменялась в версиях Visual C++ от 4-й до 6-й. Однако это не означает, что данная структура не будет изменена в будущей версии Visual C++. При использовании расширения MemDumperValidator нужно быстро проверять каждый пакет обслуживания и главный релиз компилятора (major release of the compiler), чтобы видеть, изменились ли их внутренние структуры.

Для того чтобы непосредственно использовать DBGINT.H, можно заменить определение структуры в файле CRTDBG_INTERNALS.H директивой #indude DBGINT.H. В этом случае нужно будет также добавить каталог <VC98>\CRT\SRC как к главной переменной окружения INCLUDE, так и в список каталогов Include-файлов на вкладке Directories диалогового окна Options из IDE Visual C++. Поскольку не каждый разработчик устанавливает исходный код CRT-библиотеки, а многие не упоминают об этой структуре в файле README.TXT, то было принято решение использовать прямое включение определения этой структуры.


Листинг 15-3.CRTDBG_INTERNALS.H

/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

"Debugging Applications" (Microsoft Press)

Copyright (c) 1997-2000 John Robbins — All rights reserved.

- - - - - - - - - - - - - - - - - - - - - - - - -  - - - -*/

#ifndef _CRTDBG_INTERNALS_H

#define _CRTDBG_INTERNALS_H 

#define nNoMansLandSize 4

 typedef struct _CrtMemBlockHeader

 {

struct _CrtMemBlockHeader * pBlockHeaderNext ;

struct _CrtMemBlockHeader * pBlockHeaderPrev ;

char * szFileName ; 

int nLine ;

 size_t nDataSize ;

 int nBlockUse ;

 long IRequest ; 

unsigned char gap[nNoMansLandSize] ;

  /* followed by:

* unsigned char data[nDataSize];

* unsigned char anotherGap[nNoMansLandSize];

*/

} _CrtMemBlockHeader; 

#define pbData(pblock) ((unsigned char *) \

((_CrtMemBlockHeader *)pblock + 1) )

tfdefine pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-l) 

#endif // _CRTDBG_INTERNALS_H

Можно также использовать определение структуры _CrtMemBiockHeader, чтобы получить дополнительную информацию из структур _crtMemstate, возвращаемых функцией _crtMemCheckpoint, потому что первым элементом в этой структуре является указатель на _crtMemBiockHeader. Надеюсь, что будущая версия DCRT-библиотеки обеспечит реальные функции доступа для получения информации о блоках памяти.

Просматривая поставляемый на сопровождающем компакт-диске исходный код файла MEMDUMPERVALIDATOR.CPP, являющегося частью проекта BUGSLAYERUTIL.DLL, можно заметить, что для внутреннего управления памятью применяются API-функции семейства HeapCreate, напрямую работающие с кучей. Причина в том, что функции дампов и hook-функции, используемые с DCRT-библиотекой, будут вызываться повторно, если применяются подпрограммы стандартной библиотеки. Имейте в виду, что мы не говорим о многопоточных повторных входах. Если код обработчика распределяет память с помощью обращения к функции malloc, то он будет введен повторно, потому что hook-функции вызываются при каждом распределении памяти.



Содержание раздела