Techniki testowania MFC
Jeśli debugowania programu MFC, może być przydatne tych metod debugowania.
W tym temacie:
AfxDebugBreak
makra śledzenia
wykrywania przecieków pamięci w MFC
Śledzenie alokacji pamięci
Włączanie Diagnostyka pamięci
Otwieranie migawki pamięci
Przeglądanie statystyki pamięci
Dokonuje zrzutu obiektu pobierania
Interpretacja pamięci dokonuje zrzutu
Dokonuje zrzutu Dostosowywanie obiektu
Zmniejszyć rozmiar Kompilacja debugowania MFC
- Tworzenie aplikacji MFC z informacje debugowania dla wybranych modułów
AfxDebugBreak
MFC zapewnia specjalny interfejs AfxDebugBreak funkcję w celu przerwania zakodowane na stałe w kodzie źródłowym:
AfxDebugBreak( );
Na platformach Intel AfxDebugBreak tworzy następujące kodu, które przerwy w źródle kodu zamiast kodu jądra:
_asm int 3
W innych platform AfxDebugBreak jedynie wywołuje DebugBreak.
Należy usunąć AfxDebugBreak instrukcji podczas tworzenia wersji tworzenia lub użyj #ifdef _DEBUG otaczającego je.
W tym temacie
Makra śledzenia
Aby wyświetlić komunikaty z programu debugera okno dane wyjściowe, można użyć ATLTRACE makra lub MFC śledzenia makra.Podobnie jak potwierdzeń, makra śledzenia są aktywne tylko wtedy, gdy wersja debugowania programu i zniknąć po skompilowany w wersji.
Poniższe przykłady pokazują niektóre sposoby używania śledzenia makra.Podobnie jak printf, śledzenia makra może obsłużyć liczba argumentów.
int x = 1;
int y = 16;
float z = 32.0;
TRACE( "This is a TRACE statement\n" );
TRACE( "The value of x is %d\n", x );
TRACE( "x = %d and y = %d\n", x, y );
TRACE( "x = %d and y = %x and z = %f\n", x, y, z );
Makra śledzenia prawidłowo obsługuje zarówno char * i wchar_t * parametrów.Poniższe przykłady pokazują, użyj makra śledzenia, łącznie z różnych typów parametrów ciągu.
TRACE( "This is a test of the TRACE macro that uses an ANSI string: %s %d\n", "The number is:", 2);
TRACE( L"This is a test of the TRACE macro that uses a UNICODE string: %s %d\n", L"The number is:", 2);
TRACE( _T("This is a test of the TRACE macro that uses a TCHAR string: %s %d\n"), _T("The number is:"), 2);
Aby uzyskać więcej informacji na temat śledzenia makra, zobacz Usługi diagnostyczne.
W tym temacie
Wykrywanie pamięci przeciek w MFC
MFC zawiera klasy i funkcji wykrywania pamięci, który jest przydzielony, ale nigdy nie alokację.
Śledzenie alokacji pamięci
W MFC, można użyć makra DEBUG_NEW zamiast właściwości Nowy przeciek operatora do lokalizowania pamięci.W wersji debugowania programu DEBUG_NEW przechowuje informacje o pliku nazwy i numerem wiersza dla każdego obiektu, który go przydziela.Kompilacja w wersji programu, DEBUG_NEW jest rozpoznawana jako prosty Nowy operacji bez nazwy i wierszu numer informacje o pliku.W związku z tym wykupić nie kary szybkość w wersji programu.
Jeśli nie chcesz ponownie zapisuje całego programu do użycia DEBUG_NEW zamiast właściwości Nowy, można zdefiniować tego makra w plikach źródłowych:
#define new DEBUG_NEW
Po wykonaniu zrzutu obiektu, każdy obiekt przydzielone z DEBUG_NEW Wyświetla liczbę plików i linii gdzie zostało przydzielone, co umożliwia wskazanie źródła przecieków pamięci.
Używa wersji debugowania struktury MFC DEBUG_NEW automatycznie, ale nie w kodzie.Jeśli chcesz skorzystać z DEBUG_NEW, należy użyć DEBUG_NEW jawnie lub #define nowy jak pokazano powyżej.
W tym temacie
Włączanie Diagnostyka pamięci
Aby móc używać funkcji Diagnostyka pamięci, należy włączyć diagnostyczne śledzenia.
Aby włączyć lub wyłączyć Diagnostyka pamięci
- Wywoływanie funkcji globalnych AfxEnableMemoryTracking Aby włączyć lub wyłączyć alokatora diagnostycznych.Ponieważ diagnostyki pamięci są domyślnie w bibliotece debugowania, będą używane są zwykle ta funkcja tymczasowo je wyłączyć, przyspiesza wykonywanie programu i zmniejsza dane wyjściowe diagnostyki.
Aby wybrać określone pamięci funkcji diagnostycznych z afxMemDF
Jeśli chcesz bardziej precyzyjną kontrolę nad funkcji diagnostycznych pamięci, można wybiórczo włączyć pamięci poszczególnych funkcji diagnostycznych i wyłączanie funkcji przez ustawienie wartości zmiennych globalnych MFC afxMemDF.Ta zmienna może mieć następujące wartości określony przez wyliczany typ afxMemDF.
Wartość
Opis
allocMemDF
Włącz diagnostyczne alokatora (domyślnie).
delayFreeMemDF
Opóźnienie zwalnianiu pamięci podczas wywoływania delete lub free aż do zakończenia programu.Spowoduje to programu można przydzielić maksymalna ilość pamięci.
checkAlwaysMemDF
Wywołanie AfxCheckMemory za każdym razem, gdy pamięci jest przydzielany lub zwalniane.
Te wartości można w połączeniu za pomocą operacji operatora logicznego OR, jak pokazano poniżej:
afxMemDF = allocMemDF | delayFreeMemDF | checkAlwaysMemDF;
W tym temacie
Otwieranie migawki pamięci
Utwórz CMemoryState obiektu, a następnie wywołać CMemoryState::Checkpoint funkcji elementu członkowskiego.Spowoduje to utworzenie pierwszą migawkę pamięci.
Po program wykonuje operacje alokacji i dezalokacji jego pamięci, należy utworzyć inny CMemoryState obiektu, a następnie wywołać Checkpoint dla tego obiektu.To pobiera migawkę drugi zużycie pamięci.
Tworzenie innego CMemoryState obiektu, a następnie wywołać jej CMemoryState::Difference funkcji elementu członkowskiego, dostarczanie jako argumenty dwa poprzednie CMemoryState obiektów.W przypadku różnic między Stanami dwóch pamięci Difference funkcja zwraca wartość różną od zera.Oznacza to, że który niektórych bloków pamięci ma nie alokację.
W tym przykładzie pokazano, jak wygląda kod:
// Declare the variables needed #ifdef _DEBUG CMemoryState oldMemState, newMemState, diffMemState; oldMemState.Checkpoint(); #endif // Do your memory allocations and deallocations. CString s("This is a frame variable"); // The next object is a heap object. CPerson* p = new CPerson( "Smith", "Alan", "581-0215" ); #ifdef _DEBUG newMemState.Checkpoint(); if( diffMemState.Difference( oldMemState, newMemState ) ) { TRACE( "Memory leaked!\n" ); } #endif
Należy zauważyć, że instrukcje sprawdzania pamięci jest oddzielona przez #ifdef_DEBUG/ #endif blokuje tak, aby ich kompilacja jest tylko wtedy, gdy wersje programu do debugowania.
Teraz, wiedząc już występuje przeciek pamięci, można użyć innej funkcji elementu członkowskiego, CMemoryState::DumpStatistics który pomoże Ci go znaleźć.
W tym temacie
Przeglądanie statystyki pamięci
CMemoryState::Difference funkcji sprawdza dwa obiekty stan pamięci i wykrywa wszystkie obiekty nie alokację ze stosu między Stanami początku i na końcu.Po wykonywane migawki pamięci i porównywany je przy użyciu CMemoryState::Difference, można wywołać metodę CMemoryState::DumpStatistics można pobrać informacji o obiektach, które nie zostały alokację.
Należy wziąć pod uwagę następującym przykładzie:
if( diffMemState.Difference( oldMemState, newMemState ) )
{
TRACE( "Memory leaked!\n" );
diffMemState.DumpStatistics();
}
Zrzut próbki z przykładu wygląda następująco:
0 bytes in 0 Free Blocks
22 bytes in 1 Object Blocks
45 bytes in 4 Non-Object Blocks
Largest number used: 67 bytes
Total allocations: 67 bytes
Bezpłatne bloki są bloków, którego dezalokacji zostanie opóźnione, jeśli afxMemDF została ustawiona w delayFreeMemDF.
Obiekt zwykłych bloków, pokazywaną w drugiej linii pozostają przydzielone na stosu.
Bloki obiektu nie obejmują tablic i struktury przydzielone z new.W takim przypadku cztery bloki niebędących obiektami przydzielone stosu, ale nie alokację.
Largest number used zapewnia maksymalną ilość pamięci używane przez program w dowolnym momencie.
Total allocations zapewnia całkowitej ilości pamięci używanej przez program.
W tym temacie
Dokonuje zrzutu obiektu pobierania
W programie MFC, można użyć CMemoryState::DumpAllObjectsSince do zrzut opis wszystkich obiektów na stosu, które nie zostały alokację.DumpAllObjectsSince dokonuje zrzutu wszystkie obiekty przydzielonych od czasu ostatniego CMemoryState::Checkpoint.Jeśli nie Checkpoint wywołanie miało miejsce, DumpAllObjectsSince dokonuje zrzutu wszystkie obiekty i nonobjects aktualnie w pamięci.
[!UWAGA]
Aby móc używać dumpingu obiektu MFC, musisz wykonać następujące czynności umożliwia śledzenie diagnostyczne.
[!UWAGA]
MFC automatycznie dokonuje zrzutu przecieku wszystkich obiektów, gdy program kończy pracę, dzięki czemu nie trzeba utworzyć kod w celu zrzut obiektów w tym momencie.
Poniższy kod sprawdza przeciek pamięci, porównując dwustanowy pamięci i dokonuje zrzutu wszystkie obiekty w przypadku wykrycia przeciek.
if( diffMemState.Difference( oldMemState, newMemState ) )
{
TRACE( "Memory leaked!\n" );
diffMemState.DumpAllObjectsSince();
}
Zawartość zrzut wyglądać w następujący sposób:
Dumping objects ->
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
{1} strcore.cpp(80) : non-object block at $00A7516E, 25 bytes long
Liczby w nawiasy na początku większości wierszy określić kolejność, w którym przydzielone obiekty.Obiekt najbardziej ostatnio przydzielone najwyższym numerze i pojawi się u góry zrzut.
Aby uzyskać maksymalną ilość informacji poza zrzutu obiektu, można zastąpić Dump funkcji elementu członkowskiego w dowolnej CObject-opracowane na obiekt, aby dostosować zrzutu obiektu.
Można ustawić punkt przerwania na alokacji pamięci określonego przez ustawienie zmiennej globalnej _afxBreakAlloc do liczby podanej w nawiasy klamrowe.Jeśli ponownie program debuger spowoduje przerwanie wykonywania po tej alokacji.Następnie można wyświetlać na stosie wywołań, aby zobaczyć, jak program, które otrzymały do tego punktu.
Biblioteka czasu C ma podobne funkcje, _CrtSetBreakAlloc, można użyć dla C alokacji czasu wykonywania.
W tym temacie
Interpretacja pamięci dokonuje zrzutu
Przyjrzyj się zrzutu ten obiekt bardziej szczegółowo:
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
{1} strcore.cpp(80) : non-object block at $00A7516E, 25 bytes long
Program wygenerowania tego zrzutu istniało tylko dwóch alokacji jawnego — na stosie i jeden na stosu:
// Do your memory allocations and deallocations.
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
CPerson Konstruktor ma trzy argumentów, które są wskaźnikami do char, które są używane do zainicjowania CString zmienne elementu członkowskiego.Zrzut pamięci zawiera CPerson obiektu wraz z trzech bloków nonobject (3, 4 i 5).Są przechowywane na znaki CString Zmienne składowe i nie można usunąć po CPerson destruktora obiektu jest wywoływany.
Liczba bloków 2 jest CPerson sam obiekt.$51A4 reprezentuje adres bloku i następuje zawartości obiektu, które zostały danych wyjściowych przez CPerson::Dump wtedy, gdy DumpAllObjectsSince.
Przypuszczalnie, że bloku o numerze 1 jest skojarzony z CString Zmienna ramki z powodu jej numer sekwencji i wielkości, która jest zgodna z liczbą znaków w ramach CString zmiennej.Zmienne przydzielone ramki są automatycznie alokację, gdy ramki wykracza poza zakres.
Zmienne ramki
Ogólnie rzecz biorąc mogą nie występować obiektów stosu skojarzone z ramki zmienne, ponieważ są automatycznie alokację zmienne ramki przejścia poza zakresem.Aby uniknąć bałaganu w swojej diagnostyczne zrzuty pamięci, należy umieścić wywołaniami Checkpoint aby były poza zakresem ramki zmiennych.Na przykład umieścić w nawiasach zakresu poprzedni kod alokacji, jak pokazano poniżej:
oldMemState.Checkpoint();
{
// Do your memory allocations and deallocations ...
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
}
newMemState.Checkpoint();
Z zakresu nawiasów w miejscu zrzut pamięci w tym przykładzie są następujące:
Dumping objects ->
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
Przydziały nonobject
Zwróć uwagę, że są niektóre obiekty (takie jak CPerson), a inne alokacji nonobject. "Nonobject "są alokacji dla obiektów nie pochodzi od CObject lub przydziałów typów pierwotnych C, takich jak char, int, lub długi.Jeśli CObject -klasy pochodnej przydziela dodatkowe miejsce, takie jak dla wewnętrznego buforów tych obiektów wyświetla zarówno obiekt, jak i nonobject alokacji.
Zapobieganie przecieków pamięci
Uwaga w kodzie powyżej, że blok pamięci skojarzone z CString zmiennej ramki automatycznie alokację i nie jest wyświetlany jako przeciek pamięci.Automatyczne dezalokacji skojarzone z zakresu reguły wykonuje te automatycznie skojarzone z zmienne ramki większością przecieków pamięci.
Przydzielone stosu obiektów jednak należy jawnie usunąć obiekt, aby zapobiec przeciek pamięci.Aby wyczyścić ostatnich przeciek pamięci w poprzednim przykładzie, Usuń CPerson obiektu przydzielone na stosie, w następujący sposób:
{
// Do your memory allocations and deallocations.
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
delete p;
}
W tym temacie
Dokonuje zrzutu Dostosowywanie obiektu
Kiedy wyprowadzić klasę z CObject, można zastąpić Dump funkcji elementu członkowskiego, aby podać dodatkowe informacje, gdy użytkownik korzysta z DumpAllObjectsSince do obiektów zrzutu okno dane wyjściowe.
Dump Funkcji zapisuje tekstowa reprezentacja elementu członkowskiego obiektu zmienne w kontekście zrzutu (CDumpContext).Kontekst zrzutu jest podobny do strumienia we/wy.Można użyć operatora Dołącz (<<) do wysyłania danych do CDumpContext.
Jeśli zastąpienie Dump funkcji, należy najpierw wywołać wersja klasa podstawowa Dump do zrzutu zawartości obiektu klasy podstawowej.Następnie danych wyjściowych tekstowy opis i wartości poszczególnych zmiennych elementu członkowskiego klasy pochodnej.
Deklaracja Dump funkcji wygląda następująco:
class CPerson : public CObject
{
public:
#ifdef _DEBUG
virtual void Dump( CDumpContext& dc ) const;
#endif
CString m_firstName;
CString m_lastName;
// And so on...
};
Ponieważ tylko obiekt dumpingu najwygodniejszy sposób podczas debugowania programu, deklaracji Dump funkcji jest oddzielona z #ifdef _DEBUG / #endif bloku.
W poniższym przykładzie Dump pierwszego wywołania funkcji Dump funkcji dla danej klasy podstawowej.Następnie zapisuje krótki opis poszczególnych zmiennych elementu członkowskiego wraz z wartości element członkowski w strumieniu diagnostycznych.
#ifdef _DEBUG
void CPerson::Dump( CDumpContext& dc ) const
{
// Call the base class function first.
CObject::Dump( dc );
// Now do the stuff for our specific class.
dc << "last name: " << m_lastName << "\n"
<< "first name: " << m_firstName << "\n";
}
#endif
Należy podać CDumpContext argument, aby określić, gdzie dane wyjściowe zrzutu.Wersja debugowania MFC dostarcza wstępnie zdefiniowanych CDumpContext obiekt o nazwie afxDump który wysyła dane wyjściowe do debugowania.
CPerson* pMyPerson = new CPerson;
// Set some fields of the CPerson object.
//...
// Now dump the contents.
#ifdef _DEBUG
pMyPerson->Dump( afxDump );
#endif
W tym temacie
Zmniejszyć rozmiar Kompilacja debugowania MFC
Informacje o debugowaniu dla dużych aplikacji MFC może zająć dużo miejsca na dysku.Można użyć jednej z tych procedur, aby zmniejszyć rozmiar:
Odbudowy bibliotek MFC przy użyciu /Z7, /Zi, /ZI (Format informacji o debugowaniu) opcji, zamiast /Z7.Te opcje tworzenia plik bazy danych (PDB) jeden program, który zawiera informacje debugowania dla całej biblioteki zmniejszenie redundancji i zapisywanie miejsca.
Odbudowy biblioteki MFC bez informacje o debugowaniu (nie /Z7, /Zi, /ZI (Format informacji o debugowaniu) opcji).W przypadku braku informacje debugowania uniemożliwi przy użyciu większości funkcji debuger kodem biblioteki MFC, ale ponieważ biblioteki MFC są już dokładnie debugowane, nie może to być problem.
Tworzenie aplikacji z informacjami debugowania dla wybranych modułów tylko opisane poniżej.
W tym temacie
Tworzenie aplikacji MFC z informacje debugowania dla wybranych modułów
Tworzenie wybrane moduły z bibliotekami debugowania MFC umożliwia na używanie krok i inne urządzenia debugowania w tych modułach.Ta procedura korzysta z obu debugowania i zwolnij tryby Visual C++ pliku reguł programu make, co wymaga zmiany opisane w następujące kroki (i również wprowadzania "odbudowy wszystkie" konieczne, gdy wymagany jest pełny kompilacji wersji).
W Eksploratorze rozwiązań wybierz projekt.
Z widoku menu, wybierz opcję stron właściwości.
Najpierw należy utworzyć nową konfigurację projektu.
W < projektu > strony właściwości okno dialogowe, kliknij przycisk programu Configuration Manager przycisku.
W okno dialogowe programu Configuration Manager, Znajdź projekt w siatce.W konfiguracji kolumny, wybierz opcję < nowy... >.
W okno dialogowe Nowy projekt konfiguracji, wpisz nazwę dla nowej konfiguracji, takich jak "Częściowy debugowanie", w Nazwa konfiguracji projektu pola.
W Skopiuj ustawienia z wybierz wersji.
Kliknij przycisk OK zamknąć nowej konfiguracji projektuokno dialogowe.
Zamknij programu Configuration Manager okno dialogowe.
Teraz zostanie ustawiony opcje dla całego projektu.
W stron właściwości okna dialogowego Właściwości konfiguracji folder, wybierz opcję Ogólne kategorii.
W siatce ustawień projektu, rozwiń węzeł domyślne ustawienia projektu (w razie potrzeby).
W obszarze domyślne ustawienia projektu, Znajdź Użyj MFC.Bieżące ustawienia pojawia się w prawej kolumnie siatki.Polecenie bieżące ustawienia i zmień je do MFC używany w bibliotece statycznych.
W lewym okienku stron właściwości po otwarciu okna dialogowego C/C++ i wybierz polecenie preprocesora.W siatce właściwości Znajdź Definicje preprocesora i Zamień "NDEBUG" na "_DEBUG".
W lewym okienku stron właściwości po otwarciu okna dialogowego Łączenie i wybierz polecenie dane wejściowe kategorii.W siatce właściwości Znajdź dodatkowe zależności.W dodatkowe zależności ustawienie, wpisz "NAFXCWD.Biblioteka"i"LIBCMT."
Kliknij przycisk OK Aby zapisać nowe opcje kompilacji i zamknąć stron właściwości okno dialogowe.
Z Tworzenie menu, wybierz opcję odbudowy.Usuwa wszystkie informacje debugowania z moduły, ale nie ma wpływu na bibliotece MFC.
Teraz należy dodać informacje debugowania do wybranych modułów w aplikacji.Należy pamiętać, że można Ustawianie punktów przerwania i korzystać z innych funkcji debuger tylko wtedy, gdy modułów, które zostały skompilowane informacje debugowania.Dla każdego pliku projektu, w którym mają zostać uwzględnione informacje debugowania, wykonaj następujące kroki:
Otwórz w Eksploratorze rozwiązań plikach źródłowych folder umieszczony w projekcie.
Wybierz plik, aby ustawić informacje debugowania dla.
Z widoku menu, wybierz opcję stron właściwości.
W stron właściwości okna dialogowego Ustawienia konfiguracji folder, otwórz C/C++ następnie wybierz folder Ogólne kategorii.
W siatce właściwości Znajdź Format informacje debugowania.
Kliknij przycisk Format informacje debugowania ustawienia, a następnie wybierz odpowiednią opcję (zazwyczaj /ZI) dla informacje debugowania.
Jeśli używasz aplikacji wygenerowane przez Kreatora aplikacji lub mieć wstępnie skompilowany nagłówki, należy wyłączyć wstępnie skompilowanej nagłówki lub ponownie skompilować je przed kompilacji w innych modułach.W przeciwnym razie zostanie wyświetlony ostrzeżenie C4650 i komunikat o błędzie C2855.Można wyłączyć wstępnie skompilowanej nagłówki, zmieniając tworzenia/używania wstępnie skompilowanej nagłówki w < projektu > właściwości okno dialogowe (Właściwości konfiguracji folderu, C/C++ podfolderu, wstępnie skompilowanej nagłówki kategorii).
Z Tworzenie menu, wybierz opcję Tworzenie odbudować pliki projektu, które są nieaktualne.
Jako alternatywę dla techniki opisane w tym temacie, można użyć pliku reguł programu make zewnętrznego do definiowania poszczególne opcje dla każdego pliku.W takim przypadku można połączyć z bibliotekami debugowania MFC, musisz zdefiniować _DEBUG Flaga dla każdego modułu.Jeśli chcesz użyć MFC wersji biblioteki, należy zdefiniować NDEBUG.Aby uzyskać więcej informacji na temat pisania zewnętrzne pliki reguł programu make, zobacz odwołania NUPEWNIJ.
W tym temacie