Методы отладки CRT
При отладке программы, используюющей библиотеку времени выполнения C, эти методы отладки могут оказаться полезными.
Использование библиотеки отладки CRT
Библиотека среды выполнения C (CRT) обеспечивает обширную поддержку отладки. Чтобы использовать одну из библиотек отладки CRT, необходимо связаться с и скомпилировать с /DEBUG
/MDd
помощью , /MTd
или ./LDd
Основные определения и макросы для отладки CRT можно найти в файле заголовка<crtdbg.h>
.
Функции в отладочных библиотеках CRT компилируются с отладочными сведениями (/Z7, /Zd, /Zi, /ZI (формат отладочной информации)) и без оптимизации. Некоторые функции содержат утверждения для проверки передаваемых им параметров, для них приведен исходный код. Исходный код позволяет войти в функцию CRT, чтобы убедиться, что она работает в соответствии с ожиданиями, а также проверить функцию на наличие некорректных параметров или состояний памяти. (Некоторые технологии CRT являются частными и не предоставляют исходный код для обработки исключений, плавающей запятой и нескольких других подпрограмм.)
Дополнительные сведения о различных библиотеках времени выполнения см. в разделе Библиотеки времени выполнения C.
Макросы для создания отчетов
Для отладки можно использовать макросы и _RPTFn
макросы, определенные в<crtdbg.h>
, для замены использования инструкцийprintf
._RPTn
Вам не нужно заключать их в #ifdef
директивы, так как они автоматически исчезают в сборке выпуска, когда _DEBUG
они не определены.
Макрос | Description |
---|---|
_RPT0 , , _RPT1 _RPT2 , _RPT3 _RPT4 |
Выводит строку сообщения и от нуля до четырех аргументов. Для _RPT1 этого _RPT4 строка сообщения служит строкой форматирования в стиле печати для аргументов. |
_RPTF0 , , _RPTF1 _RPTF2 , _RPTF3 _RPTF4 |
То же, что _RPTn и макросы, но эти макросы также выводит имя файла и номер строки, где находится макрос. |
Рассмотрим следующий пример:
#ifdef _DEBUG
if ( someVar > MAX_SOMEVAR )
printf( "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n",
someVar, otherVar );
#endif
Этот код выводит значения someVar
и otherVar
в stdout
. Вызов _RPTF2
можно применить для отчета об этих значениях плюс имя файла и номер строки:
if (someVar > MAX_SOMEVAR) _RPTF2(_CRT_WARN, "In NameOfThisFunc( ), someVar= %d, otherVar= %d\n", someVar, otherVar );
Некоторым приложениям может потребоваться отладочная отчетность о том, что макросы, предоставленные библиотекой времени выполнения C, не предоставляются. В таком случае можно написать свой собственный макрос, удовлетворяющий конкретным требованиям. Например, в одном из файлов заголовков можно включить следующий код, чтобы определить макрос с именем ALERT_IF2
:
#ifndef _DEBUG /* For RELEASE builds */
#define ALERT_IF2(expr, msg, arg1, arg2) do {} while (0)
#else /* For DEBUG builds */
#define ALERT_IF2(expr, msg, arg1, arg2) \
do { \
if ((expr) && \
(1 == _CrtDbgReport(_CRT_ERROR, \
__FILE__, __LINE__, msg, arg1, arg2))) \
_CrtDbgBreak( ); \
} while (0)
#endif
Один вызов ALERT_IF2
может выполнять все функции printf
кода:
ALERT_IF2(someVar > MAX_SOMEVAR, "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n", someVar, otherVar );
Пользовательский макрос можно легко настроить для передачи большего или меньшего объема информации в разные места назначения. Этот подход полезен при развитии требований к отладке.
Написание функций отладочных ловушек
Вы можете написать несколько типов пользовательских функций перехватчика отладки, которые позволяют вставлять код в некоторые предопределенные точки в обычной обработке отладчика.
Функции-ловушки клиентского блока
Если нужно проверить или вывести данные, хранящиеся в блоках типа _CLIENT_BLOCK
, можно написать для этого специальную функцию. Функция, написаемая, должна иметь прототип, аналогичный следующему, как определено в<crtdbg.h>
:
void YourClientDump(void *, size_t)
Другими словами, функция перехватчика должна принимать void
указатель на начало блока выделения, а также size_t
значение типа, указывающее размер выделения и возвращаемое void
значение. В противном случае его содержимое до вас.
После установки функции перехватчика с помощью _CrtSetDumpClient он будет вызываться каждый раз при _CLIENT_BLOCK
дампах блока. _CrtReportBlockType можно применять для получения сведений о типе или подтипе выводимых блоков.
Указатель на передаваемую _CrtSetDumpClient
функцию имеет тип _CRT_DUMP_CLIENT
, как определено в<crtdbg.h>
:
typedef void (__cdecl *_CRT_DUMP_CLIENT)
(void *, size_t);
Функции-ловушки выделения
Функция перехватчика выделения, установленная с помощью _CrtSetAllocHook
, вызывается при каждом выделении памяти, перемещении или освобождении. Этот тип ловушек можно использовать для различных целей. Используйте их, например, для проверки, как приложение обрабатывает ситуации недостатка памяти, или для оценки шаблонов выделения, или для регистрации данных о выделении для дальнейшего анализа.
Примечание.
Помните об ограничении использования функций библиотеки среды выполнения C в функции перехватчика выделения, описанном в разделе "Перехватчики выделения" и выделения памяти crt.
Функция-ловушка выделения должна иметь следующий пример:
int YourAllocHook(int nAllocType, void *pvData,
size_t nSize, int nBlockUse, long lRequest,
const unsigned char * szFileName, int nLine )
Указатель, который _CrtSetAllocHook
передается типом _CRT_ALLOC_HOOK
, как определено в<crtdbg.h>
:
typedef int (__cdecl * _CRT_ALLOC_HOOK)
(int, void *, size_t, int, long, const unsigned char *, int);
Когда библиотека времени выполнения вызывает ваш перехватчик, аргумент указывает, nAllocType
какая операция выделения будет выполнена (_HOOK_ALLOC
, _HOOK_REALLOC
или _HOOK_FREE
). В случае освобождения или перераспределения, pvData
содержит указатель на пользовательскую часть освобождаемого блока. Однако в случае выделения памяти этот указатель пуст, так как выделение еще не произошло. Остальные аргументы содержат размер выделения, тип блока, последовательный номер запроса и указатель на имя файла. Если он доступен, аргументы также включают номер строки, в которой было выполнено выделение. После того как функция перехватчика выполняет любой анализ и другие задачи, которые его автор хочет, она должна вернуться TRUE
, указывая, что операция выделения может продолжаться, или FALSE
, указывая, что операция должна завершиться ошибкой. Простой перехватчик этого типа может проверить объем памяти, выделенный до сих пор, и вернуть FALSE
, если эта сумма превысила небольшой предел. Затем приложение будет испытывать такие ошибки выделения, которые обычно возникают, только если доступная память была низкой. Более сложные ловушки могут отслеживать структуру выделения, анализировать использование памяти или сообщать о возникновении какой-либо определенной ситуации.
Выделение перехватчиков и выделений памяти CRT
Важное ограничение функций перехватчика выделения заключается в том, что они должны явно игнорировать _CRT_BLOCK
блоки. Это выделения памяти, создаваемые внутри библиотеки CRT ее функциями при любом вызове функций CRT, выделяющих внутреннюю память. Вы можете исключить блоки _CRT_BLOCK
из обработки путем добавления в начало функции-ловушки выделения следующего кода:
if ( nBlockUse == _CRT_BLOCK )
return( TRUE );
Если ловушка обрабатывает блоки _CRT_BLOCK
, то любая вызываемая в ней функция CRT может привести к выполнению в программе бесконечного цикла. Например, printf
осуществляет внутреннее выделение. Если код перехватчика вызывается printf
, то результирующее выделение приведет к повторному вызову перехватчика, который снова вызовет printf
и т. д., пока стек не переполнен. Если нужен отчет об операциях выделения _CRT_BLOCK
, есть способ обойти это ограничение — для форматирования и вывода использовать функции Windows API вместо функций CRT. Так как функции API-интерфейсов Windows не используют кучу библиотеки CRT, они не могут привести к выполнению в приложении бесконечного цикла.
При проверке исходных файлов библиотеки времени выполнения вы увидите, _CrtDefaultAllocHook
что функция перехватчика выделения по умолчанию (которая просто возвращается TRUE
), находится в отдельном файле собственного debug_heap_hook.cpp
файла. Если вы хотите, чтобы перехватчик выделения был вызван даже для выделения, сделанных кодом запуска во время выполнения перед функцией приложения main
, вы можете заменить эту функцию по умолчанию одной из собственных, а не использовать _CrtSetAllocHook
.
Отчетные функции-ловушки
Функция перехватчика отчета, установленная с помощью _CrtSetReportHook
, вызывается каждый раз, когда _CrtDbgReport
создает отчет отладки. Помимо всего прочего их можно использовать для фильтрации отчетов, которые позволяют отобрать выделения конкретного типа. Функция перехватчика отчета должна иметь прототип, как в следующем примере:
int AppReportHook(int nRptType, char *szMsg, int *retVal);
Указатель, который _CrtSetReportHook
передается в тип _CRT_REPORT_HOOK
, как определено в <crtdbg.h>
:
typedef int (__cdecl *_CRT_REPORT_HOOK)(int, char *, int *);
Когда библиотека времени выполнения вызывает функцию перехватчика, nRptType
аргумент содержит категорию отчета (_CRT_WARN
или_CRT_ASSERT
_CRT_ERROR
), szMsg
содержит указатель на полностью собранную строку сообщения отчета и retVal
указывает, следует ли _CrtDbgReport
продолжить нормальное выполнение после создания отчета или запустить отладчик. retVal
(Значение нуля продолжает выполнение, значение 1 запускает отладчик.)
Если перехватчик полностью обрабатывает сообщение, чтобы дальнейшие отчеты не требовались, он должен вернуться TRUE
. Если он возвращается FALSE
, _CrtDbgReport
сообщение будет сообщаться обычно.
В этом разделе
Версии отладки функций выделения кучи
Обсуждает специальные версии отладочных функций распределения куч, включая вызовы вызовов CRT, преимущества их явного вызова, как избежать преобразования, отслеживания отдельных типов выделения в клиентских блоках и результатов, не определяющих
_DEBUG
.-
Описывает управление памятью и отладочную кучу, типы блоков в отладочной куче, функции создания отчетов о состоянии кучи и способы отслеживания запросов на выделение.
Поиск утечек памяти с помощью библиотеки CRT
Рассматриваются способы обнаружения и изоляции утечек памяти с помощью отладчика и библиотеки времени выполнения языка C (CRT).