Techniki debugowania MFC
Jeśli debugowania programu MFC tych technik debugowania może być przydatne.
W tym temacie
AfxDebugBreak
Makro śledzenia
Wykrywanie pamięci przecieków w MFC
Śledzenie alokacji pamięci
Włączanie narzędzia Diagnostyka pamięci
Robienia zdjęć pamięci
Wyświetlanie statystyk pamięci
Zrzuca biorąc obiektu
Interpretowanie pamięci Zrzuca
Zrzuca Dostosowywanie obiektu
Zmniejszenie rozmiarów budowania MFC Debug
- Budowanie aplikacji MFC z informacjami debugowania dla wybranych modułów
AfxDebugBreak
Uwidaczniają MFC AfxDebugBreak funkcja zakodowane na stałe punkty przerwania w kodzie źródłowym:
AfxDebugBreak( );
Na platformach Intel AfxDebugBreak produkuje następujący kod, w jakich miejscach w źródle kod zamiast kodu jądra:
_asm int 3
Na innych platformach AfxDebugBreak po prostu wywołuje DebugBreak.
Należy usunąć AfxDebugBreak instrukcji podczas tworzenia jednej z wersji, zbudować lub wykorzystać #ifdef _DEBUG do ujęcia ich.
W tym temacie
Makro śledzenia
Wyświetlanie komunikatów z poziomu programu w debugerze okna dane wyjściowe, można użyć ATLTRACE makro lub MFC śledzenia makro.Jak twierdzenia, makra śledzenia są aktywne tylko w wersji debugowej tego programu i znikają, gdy kompilowany w wersji wydania.
Następujące przykłady przedstawiają niektóre sposoby korzystania z śledzenia makro.Jak printf, śledzenia makro może obsługiwać wiele 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 );
Makro śledzenia prawidłowo obsługuje zarówno znaki i wchar_t * parametry.Następujące przykłady demonstrują użycie makra śledzenia wraz z różnego rodzaju parametry 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);
Więcej informacji na temat śledzenia makro, zobacz Usługi diagnostyczne.
W tym temacie
Wykrywanie pamięci przecieków w MFC
MFC dostarcza klas i funkcje wykrywania pamięci, która jest przydzielona, ale nigdy nie cofniętych przydziałów.
Śledzenie alokacji pamięci
W MFC, można użyć makra DEBUG_NEW zamiast Nowy operator pomagających znaleźć pamięci przecieków.Za pomocą tego programu DEBUG_NEW informacje o pliku nazwa i wiersza numer dla każdego obiektu, który przydziela go.Podczas kompilowania wersji tego programu, DEBUG_NEW prosty jest rozpoznawany jako Nowy operacji bez nazwy i linii numer informacje o plikach.W ten sposób płaci się bez kary prędkości w wydanej wersji tego programu.
Jeśli nie chcesz ponownie zapisuje całego programu obsłudze DEBUG_NEW zamiast Nowy, można zdefiniować tego makra w plikach źródłowych:
#define new DEBUG_NEW
Po wykonaniu obiektu zrzutu, każdy obiekt rozdzielone z DEBUG_NEW będą zawierać numer pliku i linii, gdzie był przydzielony, pozwala na określenie źródła przecieki pamięci.
Używa wersji debugowej RAM MFC DEBUG_NEW automatycznie, ale nie w kodzie.Jeśli chcesz skorzystać z DEBUG_NEW, należy użyć DEBUG_NEW jawnie lub # definiować nowe jak wykazano powyżej.
W tym temacie
Włączanie narzędzia Diagnostyka pamięci
Przed użyciem urządzenia Diagnostyka pamięci, należy włączyć śledzenie diagnostycznych.
Aby włączyć lub wyłączyć narzędzie Diagnostyka pamięci
- Wywołanie funkcji globalnej AfxEnableMemoryTracking Aby włączyć lub wyłączyć program przydzielania pamięci diagnostycznych.Ponieważ narzędzie Diagnostyka pamięci są domyślnie w bibliotece programu debug, będzie zwykle funkcja ta tymczasowo je wyłączyć, co przyspiesza wykonywanie kodu programu i zmniejsza diagnostyczne dane wyjściowe.
Aby wybrać funkcje diagnostyczne pamięci z afxMemDF
Jeśli chcesz, aby dokładniej kontrolować funkcje diagnostyczne pamięci, można selektywnie włączyć funkcje diagnostyczne poszczególnych pamięci i wyłącza ustawiając wartość zmiennej globalnej MFC afxMemDF.Ta zmienna może mieć następujące wartości określonych przez typ wyliczeniowy afxMemDF.
Wartość
Opis
allocMemDF
Włącz program przydzielania pamięci diagnostycznych (ustawienie domyślne).
delayFreeMemDF
Opóźnienie zwalnianiu pamięci, podczas wywoływania delete lub free do momentu zamyka program.To spowoduje, że Twój program ma alokować kwoty maksymalną ilość pamięci.
checkAlwaysMemDF
Call AfxCheckMemory za każdym razem pamięci jest przydzielona lub zwolnione.
Wartości te można w połączeniu za pomocą operacji lub logicznych, jak pokazano poniżej:
afxMemDF = allocMemDF | delayFreeMemDF | checkAlwaysMemDF;
W tym temacie
Robienia zdjęć pamięci
Tworzenie CMemoryState obiektu i Rozmowa CMemoryState::Checkpoint funkcji składowej.Spowoduje to utworzenie pierwszej migawki pamięci.
Po program wykonuje operacje alokacji i dezalokacji jej pamięci, należy utworzyć inny CMemoryState obiektu i Rozmowa Checkpoint dla tego obiektu.Pobiera drugim obrazem użycia pamięci.
Tworzą trzecią CMemoryState obiektu i wywołanie jej CMemoryState::Difference funkcji składowej, dostarczanie jako argumenty dwie poprzednie CMemoryState obiektów.Jeśli istnieje różnica między Państwami dwóch pamięci Difference , funkcja zwraca wartość różną od zera.Wskazuje to, że niektóre bloków pamięci mają nie przydziałów.
Ten przykład pokazuje, jak wygląda ten 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 są oddzielona przez #ifdef_DEBUG/ #endif klocki tak, aby kompiluje się je tylko w wersjach debugowania tego programu.
Teraz, że wiesz, że występuje przeciek pamięci, można użyć innej funkcji składowej, CMemoryState::DumpStatistics, do Wyświetlanie statystyki pamięci które pomogą Ci go zlokalizować.
W tym temacie
Wyświetlanie statystyk pamięci
CMemoryState::Difference Funkcja patrzy na dwa obiekty stanu pamięci i wykrywa wszystkie obiekty nie przydziałów ze sterty między Państwami początek i koniec.Po podjęte migawek pamięci i porównywał je za pomocą CMemoryState::Difference, można wywołać CMemoryState::DumpStatistics Aby uzyskać informacje o obiektach, które nie zostały przydziałów.
Rozważmy następujący przykład:
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
Wolnych bloków są blokami którego dezalokacji jest opóźnione, jeżeli afxMemDF została ustawiona na delayFreeMemDF.
Bloki zwykłego obiektu, określone w drugim wierszu, pozostają przydzielone na stercie.
Bloki non-object obejmują tablic i struktur rozdzielone z new.W takim przypadku cztery bloki niebędących obiektami były zaalokowanego na stercie, ale nie przydziałów.
Largest number useddaje maksymalną ilość pamięci używanych przez program w dowolnym momencie.
Total allocationsdaje całkowita ilość pamięci używanej przez program.
W tym temacie
Zrzuca biorąc obiektu
W programie MFC, można użyć CMemoryState::DumpAllObjectsSince zrzucić opis wszystkich obiektów na stercie, które nie zostały przydziałów.DumpAllObjectsSinceZrzuca wszystkie obiekty przydzielone od czasu ostatniego CMemoryState::Checkpoint.Jeśli nie Checkpoint wywołanie miało miejsce, DumpAllObjectsSince Zrzuca wszystkie obiekty i nonobjects aktualnie w pamięci.
[!UWAGA]
Przed użyciem MFC obiektu dumpingu, należy włączyć śledzenie diagnostyczne.
[!UWAGA]
MFC automatycznie Zrzuca przecieku wszystkie obiekty, kiedy kończy pracę programu, dzięki czemu nie trzeba tworzyć kod, aby zrzucić obiektów w tym punkcie.
Poniższy kod testy dla przeciek pamięci przez porównanie dwóch Państw pamięci i zrzuca wszystkie obiekty po wykryciu przeciek.
if( diffMemState.Difference( oldMemState, newMemState ) )
{
TRACE( "Memory leaked!\n" );
diffMemState.DumpAllObjectsSince();
}
Zawartość zrzutu 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 nawiasach klamrowych, na początku większości linii określić kolejność, w którym zostały przydzielone obiekty.Najbardziej niedawno zaalokowanego obiektu ma najwyższy numer i jest wyświetlany w górnej części zrzut.
Aby uzyskać maksymalną ilość informacji z zrzutu obiektu, można zastąpić Dump funkcji składowej któregokolwiek CObject-pochodzi obiekt, aby dostosować zrzutu obiektu.
Można ustawić punkt przerwania na alokację pamięci określonej przez ustawienie zmiennej globalnej _afxBreakAlloc do liczby podanej w nawiasy klamrowe.Jeśli Uruchom ponownie program debugera zostaną przerwane wykonywanie, jeżeli odbywa się przydział środków.Następnie można przejrzeć stos wywołań, aby zobaczyć, jak Twój program jeszcze do tego punktu.
Biblioteka uruchomieniowa C ma podobną funkcję, _CrtSetBreakAlloc, używania o przydział C w czasie wykonywania.
W tym temacie
Interpretowanie pamięci Zrzuca
Spójrz na ten zrzut 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, który wygenerował ten zrzut miał tylko dwie alokacje jawne — jeden na stosie, a drugi na stercie:
// 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 przyjmuje trzy parametry, które są wskaźnikami do char, które są używane do inicjowania CString zmienne składowe.Zrzutu pamięci zawiera CPerson obiektu wraz z trzech bloków nonobject (3, 4 i 5).Te posiadają znaki dla CString zmienne składowe i nie zostaną usunięte po CPerson destruktor obiektu jest wywoływany.
Numer bloku 2 jest CPerson samego obiektu.$51A4reprezentuje adres bloku i następuje zawartość obiektu, który były wyjście przez CPerson::Dump po wywołaniu DumpAllObjectsSince.
Można się domyślić, że blok o numerze 1 jest skojarzony z CString zmienna ramki z powodu jego numer sekwencyjny i wielkości, która jest zgodna z liczbą znaków w ramce CString zmiennej.Zmienne przydzielona na ramce automatycznie są wtedy, kiedy ramki wykracza poza zakres.
Zmienne ramki
Ogólnie rzecz biorąc nie powinno się martwić o obiektach sterty skojarzonych z ramki zmiennych, ponieważ są one automatycznie wtedy, kiedy zmienne ramki Przejdź poza zakresem.Aby uniknąć bałaganu w swojej diagnostyczne zrzutów pamięci, należy umieoć wywołaniami Checkpoint tak, że są one poza zakres zmiennych ramki.Zakres nawiasy powyższy kod alokacji, należy umieścić na przykład, 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 klamrami zakres w miejsce zrzutu pamięci w tym przykładzie jest następująca:
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
Alokacje nonobject
Zauważ, że niektóre alokacje są obiekty (takie jak CPerson), a niektóre są alokacje nonobject. "Alokacje nonobject"są alokacje dla obiektów nie pochodzi od CObject lub alokacje typów pierwotnych C, takich jak char, int, lub długi.Jeśli CObject -klasie pochodnej przydziela miejsce dodatkowe, takie jak dla bufory wewnętrzne tych obiektów pokaże alokacje zarówno obiekt, jak i nonobject.
Zapobieganiu wycieki pamięci
Zawiadomienie w powyższym kodzie, że blok pamięci skojarzony z CString zmienna ramka automatycznie został cofniętych przydziałów i nie jest wyświetlany jako przeciek pamięci.Automatyczne dezalokacji, związane z od zasięgu zajmuje większość wycieki pamięci skojarzonej ze zmiennymi ramki.
W przypadku obiektów zaalokowanego na stercie jednak należy usunąć jawnie obiektu, aby zapobiec przeciek pamięci.Aby wyczyścić ostatnią przeciek pamięci w poprzednim przykładzie, Usuń CPerson obiektu rozdzielony 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
Zrzuca Dostosowywanie obiektu
Kiedy użytkownik pochodzić od klasy z CObject, można zastąpić Dump funkcji składowej, aby podać dodatkowe informacje, korzystając z DumpAllObjectsSince do obiektów zrzutu do okna dane wyjściowe.
Dump Funkcja zapisuje tekstowa reprezentacja obiektu Członkowskiego zmienne kontekstu zrzutu (CDumpContext).Kontekst zrzutu jest podobne do strumienia wejścia/wyjścia.Można użyć operatora append (<<) do wysyłania danych do CDumpContext.
Kiedy można zastąpić Dump funkcji, należy najpierw wywoływać klasy podstawowej wersję Dump do zrzutu zawartości obiektu klasy podstawowej.Następnie należy utworzyć tekstowy opis i wartość dla każdej zmiennej Członkowskich klasy pochodnej.
Deklaracja Dump funkcja wygląda tak:
class CPerson : public CObject
{
public:
#ifdef _DEBUG
virtual void Dump( CDumpContext& dc ) const;
#endif
CString m_firstName;
CString m_lastName;
// And so on...
};
Ponieważ obiekt dumpingu sens tylko podczas debugowania programu, deklaracji Dump funkcja jest oddzielona z #ifdef _DEBUG / #endif bloku.
W poniższym przykładzie Dump pierwszy wywołań funkcji Dump funkcja dla swojej klasie podstawowej.Następnie zapisuje krótki opis każdej ze zmiennych Członkowskich wraz z wartością elementu członkowskiego 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ć, dokąd zrzutu danych wyjściowych.Wstępnie zdefiniowane dostarcza do programu MFC CDumpContext obiektu o nazwie afxDump , który wysyła dane wyjściowe do debugera.
CPerson* pMyPerson = new CPerson;
// Set some fields of the CPerson object.
//...
// Now dump the contents.
#ifdef _DEBUG
pMyPerson->Dump( afxDump );
#endif
W tym temacie
Zmniejszenie rozmiarów budowania MFC Debug
Informacje o debugowaniu, w przypadku dużych aplikacji MFC może zajmować dużo miejsca na dysku.Można użyć jednej z tych procedur, aby zmniejszyć rozmiar:
Przebudować biblioteki MFC, przy użyciu / Z7, /Zi, /ZI (Format informacji debugowania) opcji, a nie /Z7.Te opcje tworzenia pliku bazy danych (PDB) jednego programu, który zawiera informacje dla całej biblioteki, zmniejszenia redundancji i oszczędność miejsca.
Przebudować biblioteki MFC bez informacje o debugowaniu (nie / Z7, /Zi, /ZI (Format informacji debugowania) opcja).W tym przypadku Brak informacji debugowania uniemożliwi przy użyciu większość urządzeń debuger kodem biblioteki MFC, ale ponieważ biblioteki MFC już dokładnie jest debugowanych, nie może to być problem.
Trzeba tworzyć aplikację ze informacje debugowania dla wybranych modułów tylko, jak opisano poniżej.
W tym temacie
Budowanie aplikacji MFC z informacjami debugowania dla wybranych modułów
Budowania wybranych modułów z biblioteki MFC debugowania umożliwia użytkownikowi korzystanie z metody i innych urządzeń debugowania w tych modułach.Procedura ta korzysta z obu debugowania i zwolnij tryby makefile Visual C++, powodując tym samym konieczność zmiany opisanych w następujących kroków (a także potrzeby podejmowania "odbudować wszystkie"), gdy wymagana jest pełna wydanej kompilacji.
W oknie Solution Explorer wybierz projekt.
Z View menu, a następnie kliknij polecenie Wybierz Stron właściwości.
Najpierw należy utworzyć nową konfigurację projektu.
W <Project> Strony właściwości okno dialogowe, kliknij przycisk Menedżer konfiguracji przycisk.
W okno dialogowe Menedżer konfiguracji, zlokalizuj w siatce projektu.W konfiguracji kolumny, wybierz < nowy... >.
W okno dialogowe Nowa konfiguracja projektu, wpisz nazwę dla nowej konfiguracji, takie jak "Częściowe debugować", w Nazwa konfiguracji projektu pole.
W Skopiuj ustawienia z listy, wybierz polecenie wersji.
Kliknij przycisk OK zamknąć Nowa konfiguracja projektuokno dialogowe.
Zamknij Menedżer konfiguracji okno dialogowe.
Teraz będzie Ustaw opcje dla całego projektu.
W Stron właściwości dialogowe, pod Właściwości konfiguracji folder, wybierz Ogólne kategorii.
W siatce projektu ustawienia rozwiń Project domyślnie (Jeśli to konieczne).
W obszarze Project domyślnie, znaleźć Wykorzystania MFC.Bieżące ustawienie pojawia się w prawej kolumnie siatki.Kliknij na bieżące ustawienie i zmień go na MFC wykorzystania w bibliotece statycznej.
W lewym okienku Stron właściwości otwarte okno dialogowe, C/C++ folder i wybierz Preprocessor.W siatce właściwości znaleźć Definicje Preprocessor i zastąpić "NDEBUG" z "_DEBUG".
W lewym okienku Stron właściwości otwarte okno dialogowe, programu łączącego folder i wybierz wprowadzania kategorii.W siatce właściwości znaleźć Dodatkowe zależności.W Dodatkowe zależności ustawienie, wpisz "NAFXCWD.LIB"i"LIBCMT".
Kliknij przycisk OK Aby zapisać nowe opcje budowania i zamknąć Stron właściwości okno dialogowe.
Z budować menu, a następnie kliknij polecenie Wybierz odbudować.Usuwa wszystkie informacje o debugowaniu z modułów, ale nie wpływa na bibliotece MFC.
Teraz należy dodać informacje o debugowaniu powrót do wybranych modułów w aplikacji.Należy pamiętać, że można ustawić punkty przerwania i wykonywać inne funkcje debugera tylko w modułach, które zostały skompilowane z informacje debugowania.Dla każdego pliku projektu, w którym mają zostać uwzględnione informacje debugowania, wykonaj następujące kroki:
W oknie Solution Explorer Otwórz Pliki źródłowe folder umieszczony w projekcie.
Wybierz plik, aby ustawić informacje debugowania.
Z View menu, a następnie kliknij polecenie Wybierz Stron właściwości.
W Stron właściwości dialogowe, pod Ustawienia konfiguracji folder, otwórz C/C++ następnie wybierz folder Ogólne kategorii.
W siatce właściwości znaleźć Program Debug: polecenie Format informacji.
Kliknij przycisk Program Debug: polecenie Format informacji ustawienia i wybierz żądaną opcję (zazwyczaj /ZI) dla informacji debugowania.
Jeśli korzystasz z aplikacji aplikacji wygenerowane przez kreatora lub mają wstępnie skompilowana nagłówki, musisz wyłączyć wstępnie skompilowanym nagłówki lub ponownie je skompilować przed kompilowania innych modułów.W przeciwnym razie otrzymasz ostrzeżenie o C4650 i C2855 komunikat o błędzie.Wstępnie skompilowanym nagłówków można wyłączyć, zmieniając Tworzenie/Używanie nagłówków wstępnie skompilowanym w <Project> Właściwości okno dialogowe (Właściwości konfiguracji folderu, C/C++ podfolder, Wstępnie skompilowana nagłówki kategorii).
Z budować menu, a następnie kliknij polecenie Wybierz budować do przebudowania plików projektów, które są nieaktualne.
Alternatywą dla techniki opisane w tym temacie, program zewnętrznego pliku makefile do zdefiniowania poszczególne opcje dla każdego pliku.W takim przypadku, aby utworzyć łącze z biblioteki MFC debugowania, należy zdefiniować _DEBUG Flaga dla każdego modułu.Jeśli chcesz korzystać z bibliotek release MFC, należy zdefiniować NDEBUG.Aby uzyskać więcej informacji na temat pisania zewnętrzne pliki reguł programu make, zobacz Odwołanie NUPEWNIJ.
W tym temacie