Techniki debugowania CRT
Podczas debugowania programu korzystającego z biblioteki czasu wykonywania języka C te techniki debugowania mogą być przydatne.
Korzystanie z biblioteki debugowania CRT
Biblioteka środowiska uruchomieniowego języka C (CRT) zapewnia rozbudowaną obsługę debugowania. Aby użyć jednej z bibliotek debugowania CRT, należy połączyć się z elementami i skompilować z elementami /DEBUG
/MDd
, /MTd
lub ./LDd
Główne definicje i makra debugowania CRT można znaleźć w pliku nagłówka<crtdbg.h>
.
Funkcje w bibliotekach debugowania CRT są kompilowane przy użyciu informacji debugowania (/Z7, /Zd, /Zi, /ZI, /ZI (Format informacji debugowania)) i bez optymalizacji. Niektóre funkcje zawierają asercji w celu zweryfikowania przekazanych do nich parametrów i podano kod źródłowy. Za pomocą tego kodu źródłowego możesz przejść do funkcji CRT, aby potwierdzić, że funkcje działają zgodnie z oczekiwaniami i sprawdzić nieprawidłowe parametry lub stany pamięci. (Niektóre technologie CRT są własnością i nie zapewniają kodu źródłowego do obsługi wyjątków, zmiennoprzecinku i kilku innych procedur).
Aby uzyskać więcej informacji na temat różnych bibliotek czasu wykonywania, których można użyć, zobacz Biblioteki czasu wykonywania języka C.
Makra raportowania
Do debugowania można użyć _RPTn
makr i _RPTFn
zdefiniowanych w<crtdbg.h>
, aby zastąpić użycie instrukcji printf
. Nie musisz ująć ich w #ifdef
dyrektywach, ponieważ automatycznie znikają w kompilacji wydania, gdy _DEBUG
nie są zdefiniowane.
Makro | opis |
---|---|
_RPT0 , , _RPT1 , _RPT2 , , _RPT3 _RPT4 |
Zwraca ciąg komunikatu i zero do czterech argumentów. W przypadku _RPT1 elementu _RPT4 ciąg komunikatu służy jako ciąg formatowania w stylu printf dla argumentów. |
_RPTF0 , , _RPTF1 , _RPTF2 , , _RPTF3 _RPTF4 |
Takie same jak _RPTn , ale te makra również wyświetlają nazwę pliku i numer wiersza, w którym znajduje się makro. |
Rozważmy następujący przykład:
#ifdef _DEBUG
if ( someVar > MAX_SOMEVAR )
printf( "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n",
someVar, otherVar );
#endif
Ten kod zwraca wartości parametrów someVar
i otherVar
do stdout
. Możesz użyć następującego wywołania, aby zgłosić _RPTF2
te same wartości, a także nazwę pliku i numer wiersza:
if (someVar > MAX_SOMEVAR) _RPTF2(_CRT_WARN, "In NameOfThisFunc( ), someVar= %d, otherVar= %d\n", someVar, otherVar );
Niektóre aplikacje mogą wymagać raportowania debugowania, które nie udostępniają makr dostarczonych z biblioteką czasu wykonywania języka C. W takich przypadkach można napisać makro zaprojektowane specjalnie zgodnie z własnymi wymaganiami. Na przykład w jednym z plików nagłówkowych możesz dołączyć kod podobny do poniższego, aby zdefiniować makro o nazwie 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
Jedno wywołanie metody może wykonać ALERT_IF2
wszystkie funkcje printf
kodu:
ALERT_IF2(someVar > MAX_SOMEVAR, "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n", someVar, otherVar );
Możesz łatwo zmienić niestandardowe makro, aby zgłosić więcej lub mniej informacji do różnych miejsc docelowych. Takie podejście jest przydatne, gdy wymagania dotyczące debugowania ewoluują.
Pisanie funkcji debugowania punktów zaczepienia
Możesz napisać kilka rodzajów niestandardowych funkcji punktu zaczepienia debugowania, które umożliwiają wstawianie kodu do niektórych wstępnie zdefiniowanych punktów wewnątrz normalnego przetwarzania debugera.
Funkcje punktu zaczepienia bloku klienta
Jeśli chcesz zweryfikować lub zgłosić zawartość danych przechowywanych w _CLIENT_BLOCK
blokach, możesz napisać funkcję specjalnie w tym celu. Napisana funkcja musi mieć prototyp podobny do następującego, zgodnie z definicją w<crtdbg.h>
:
void YourClientDump(void *, size_t)
Innymi słowy, funkcja haka powinna akceptować void
wskaźnik na początku bloku alokacji wraz z wartością typu wskazującą size_t
rozmiar alokacji i zwrócić wartość void
. W przeciwnym razie jego zawartość jest do Ciebie.
Po zainstalowaniu funkcji haka przy użyciu _CrtSetDumpClient będzie ona wywoływana za każdym razem, gdy _CLIENT_BLOCK
blok zostanie po cenach dumpingowych. Następnie możesz użyć _CrtReportBlockType , aby uzyskać informacje o typie lub podtypie bloków dumpingowych.
Wskaźnik do przekazanej funkcji _CrtSetDumpClient
ma typ _CRT_DUMP_CLIENT
, zgodnie z definicją w<crtdbg.h>
:
typedef void (__cdecl *_CRT_DUMP_CLIENT)
(void *, size_t);
Funkcje punktu zaczepienia alokacji
Funkcja punktu zaczepienia alokacji, zainstalowana przy użyciu metody _CrtSetAllocHook
, jest wywoływana za każdym razem, gdy pamięć jest przydzielana, przydzielana w sposób rzeczywisty lub zwalniana. Tego typu haka można używać w wielu różnych celach. Służy do testowania sposobu, w jaki aplikacja obsługuje niewystarczające sytuacje pamięci, takie jak badanie wzorców alokacji lub informacje o alokacji dziennika na potrzeby późniejszej analizy.
Uwaga
Należy pamiętać o ograniczeniu używania funkcji biblioteki środowiska uruchomieniowego języka C w funkcji punktu zaczepienia alokacji, opisane w temacie Alokacja haki i alokacje pamięci crt.
Funkcja punktu zaczepienia alokacji powinna mieć prototyp podobny do następującego przykładu:
int YourAllocHook(int nAllocType, void *pvData,
size_t nSize, int nBlockUse, long lRequest,
const unsigned char * szFileName, int nLine )
Wskaźnik przekazywany do _CrtSetAllocHook
jest typu _CRT_ALLOC_HOOK
, zgodnie z definicją w<crtdbg.h>
:
typedef int (__cdecl * _CRT_ALLOC_HOOK)
(int, void *, size_t, int, long, const unsigned char *, int);
Gdy biblioteka czasu wykonywania wywołuje punkt zaczepienia, argument wskazuje, nAllocType
jaka operacja alokacji ma zostać wykonana (_HOOK_ALLOC
, _HOOK_REALLOC
, lub _HOOK_FREE
). W miejscu wolnym lub w lokalizacji rzeczywistej pvData
ma wskaźnik do artykułu użytkownika bloku, który ma zostać zwolniony. Jednak w przypadku alokacji ten wskaźnik ma wartość null, ponieważ alokacja nie wystąpiła. Pozostałe argumenty zawierają rozmiar alokacji, jego typ bloku, numer żądania sekwencyjnego i wskaźnik do nazwy pliku. Jeśli są dostępne, argumenty zawierają również numer wiersza, w którym dokonano alokacji. Gdy funkcja haka wykonuje dowolną analizę i inne zadania, których chce autor, musi zwrócić TRUE
wartość , wskazującą, że operacja alokacji może kontynuować lub FALSE
, wskazując, że operacja powinna zakończyć się niepowodzeniem. Prosty hak tego typu może sprawdzić ilość pamięci przydzielonej do tej pory i zwrócić FALSE
, jeśli ta ilość przekroczyła niewielki limit. Aplikacja napotkałaby wówczas rodzaj błędów alokacji, które zwykle występują tylko wtedy, gdy dostępna pamięć była niska. Bardziej złożone haki mogą śledzić wzorce alokacji, analizować użycie pamięci lub zgłaszać, gdy wystąpią określone sytuacje.
Przypinacze alokacji i alokacje pamięci CRT
Ważnym ograniczeniem funkcji punktu zaczepienia alokacji jest to, że muszą jawnie ignorować _CRT_BLOCK
bloki. Te bloki to alokacje pamięci wykonywane wewnętrznie przez funkcje biblioteki czasu wykonywania języka C, jeśli są wykonywane wywołania funkcji biblioteki czasu wykonywania języka C, które przydzielają pamięć wewnętrzną. Bloki można zignorować _CRT_BLOCK
, dołączając następujący kod na początku funkcji punktu zaczepienia alokacji:
if ( nBlockUse == _CRT_BLOCK )
return( TRUE );
Jeśli punkt zaczepienia alokacji nie ignoruje _CRT_BLOCK
bloków, każda funkcja biblioteki czasu wykonywania języka C wywoływana w haku może wychwycić program w nieskończonej pętli. Na przykład printf
tworzy wewnętrzną alokację. Jeśli kod punktu zaczepienia wywołuje printf
metodę , wynikowa alokacja spowoduje ponowne wywołanie punktu zaczepienia, co spowoduje ponowne wywołanie printf
i tak dalej do momentu przepełnienia stosu. Jeśli musisz zgłosić _CRT_BLOCK
operacje alokacji, jednym ze sposobów obejścia tego ograniczenia jest użycie funkcji interfejsu API systemu Windows, a nie funkcji czasu wykonywania języka C do formatowania i danych wyjściowych. Ponieważ interfejsy API systemu Windows nie używają sterty biblioteki czasu wykonywania języka C, nie będą one wychwytywały haka alokacji w nieskończonej pętli.
Jeśli zbadasz pliki źródłowe biblioteki czasu wykonywania, zobaczysz, że domyślna funkcja _CrtDefaultAllocHook
punktu zaczepienia alokacji (która po prostu zwraca TRUE
wartość ), znajduje się w osobnym pliku własnego, debug_heap_hook.cpp
. Jeśli chcesz, aby punkt zaczepienia alokacji był wywoływany nawet dla alokacji wykonanych przez kod uruchamiania w czasie wykonywania przed funkcją aplikacji main
, możesz zastąpić tę funkcję domyślną własną, zamiast używać polecenia _CrtSetAllocHook
.
Raportowanie funkcji punktów zaczepienia
Funkcja punktów zaczepienia raportu, zainstalowana przy użyciu metody _CrtSetReportHook
, jest wywoływana za każdym razem, gdy _CrtDbgReport
generuje raport debugowania. Można go używać między innymi do filtrowania raportów, aby skoncentrować się na określonych typach alokacji. Funkcja punktów zaczepienia raportu powinna mieć prototyp podobny do tego przykładu:
int AppReportHook(int nRptType, char *szMsg, int *retVal);
Wskaźnik przekazywany do _CrtSetReportHook
jest typu _CRT_REPORT_HOOK
, zgodnie z definicją w pliku :<crtdbg.h>
typedef int (__cdecl *_CRT_REPORT_HOOK)(int, char *, int *);
Gdy biblioteka czasu wykonywania wywołuje funkcję hook, argument zawiera kategorię raportu (, lub ), zawiera wskaźnik do w pełni zmontowanego ciągu komunikatu raportu i retVal
określa, nRptType
czy _CrtDbgReport
należy kontynuować normalne wykonywanie po wygenerowaniu raportu, czy uruchomić debuger. szMsg
_CRT_ASSERT
_CRT_ERROR
_CRT_WARN
retVal
(Wartość zero kontynuuje wykonywanie, wartość 1 uruchamia debugera).
Jeśli punkt zaczepienia całkowicie obsłuży komunikat, aby nie było wymagane dalsze raportowanie, powinien zwrócić wartość TRUE
. Jeśli zwraca FALSE
wartość , _CrtDbgReport
będzie zgłaszać komunikat normalnie.
W tym obszarze
Wersja debugowania funkcji alokacji sterty
Omówienie specjalnych wersji debugowania funkcji alokacji sterty, w tym: sposobu wywoływania map CRT, korzyści z ich jawnego wywoływania, sposobu unikania konwersji, śledzenia oddzielnych typów alokacji w blokach klienta oraz wyników niezdefiniowania
_DEBUG
.Szczegóły dotyczące sterty debugowania CRT
Opisuje zarządzanie pamięcią i stertę debugowania, typy bloków na stercie debugowania, funkcje raportowania stanu sterty sterty oraz sposób użycia sterty debugowania do śledzenia żądań alokacji.
Znajdowanie przecieków pamięci przy użyciu biblioteki CRT
Obejmuje techniki wykrywania i izolowania przecieków pamięci przy użyciu debugera i biblioteki czasu wykonywania języka C.