CRT hata ayıklama öbeği ayrıntıları

CRT hata ayıklama yığını ve ilgili işlevler, kodunuzda bellek yönetimi sorunlarını izlemek ve hatalarını ayıklamak için birçok yol sağlar. Arabellek taşmalarını bulmak ve bellek ayırmalarını ve bellek durumunu izlemek ve raporlamak için bunu kullanabilirsiniz. Ayrıca, benzersiz uygulama gereksinimleriniz için kendi hata ayıklama ayırma işlevlerinizi oluşturma desteğine de sahiptir.

Hata ayıklama yığını ile arabellek taşmalarını bulma

Programcıların karşılaştığı en yaygın ve en önemli sorunlardan ikisi, ayrılan arabellek ve bellek sızıntılarının (artık gerekli olmadığında serbest ayırmaların başarısız olması) sonunun üzerine yazmaktır. Hata ayıklama yığını, bu türdeki bellek ayırma sorunlarını çözmek için güçlü araçlar sağlar.

Yığın işlevlerinin Hata Ayıklama sürümleri, Yayın derlemelerinde kullanılan standart veya temel sürümleri çağırır. Bir bellek bloğu istediğinizde, hata ayıklama yığını yöneticisi temel yığından istediğinizden biraz daha büyük bir bellek bloğu ayırır ve bu bloğun sizin bölümünüz için bir işaretçi döndürür. Örneğin, uygulamanızın şu çağrıyı içerdiğini varsayalım: malloc( 10 ). Yayın derlemesinde, malloc 10 bayt ayırma isteğinde bulunan temel yığın ayırma yordamını çağırır. Ancak bir Hata Ayıklama derlemesinde, malloc 10 bayt artı yaklaşık 36 bayt ek bellek ayırma isteğinde bulunan temel yığın ayırma yordamını çağıran öğesini çağırır _malloc_dbg. Hata ayıklama yığınında elde edilen tüm bellek blokları, ayrıldığı zamanlara göre sıralanmış tek bir bağlantılı listeye bağlanır.

Hata ayıklama yığını yordamları tarafından ayrılan ek bellek, muhasebe bilgileri için kullanılır. Hata ayıklama bellek bloklarını birbirine bağlayan işaretçileri ve ayrılmış bölgenin üzerine yazılmasını yakalamak için verilerinizin her iki tarafında küçük arabellekler vardır.

Şu anda, hata ayıklama yığınının muhasebe bilgilerini depolamak için kullanılan blok üst bilgisi yapısı üst bilgide <crtdbg.h> bildirilir ve CRT kaynak dosyasında tanımlanır <debug_heap.cpp> . Kavramsal olarak bu yapıya benzer:

typedef struct _CrtMemBlockHeader
{
// Pointer to the block allocated just before this one:
    _CrtMemBlockHeader* _block_header_next;
// Pointer to the block allocated just after this one:
    _CrtMemBlockHeader* _block_header_prev;
    char const*         _file_name;
    int                 _line_number;

    int                 _block_use;      // Type of block
    size_t              _data_size;      // Size of user block

    long                _request_number; // Allocation number
// Buffer just before (lower than) the user's memory:
    unsigned char       _gap[no_mans_land_size];

    // Followed by:
    // unsigned char    _data[_data_size];
    // unsigned char    _another_gap[no_mans_land_size];
} _CrtMemBlockHeader;

no_mans_land Bloğun kullanıcı veri alanının her iki tarafındaki arabelleklerin boyutu şu anda 4 bayttır ve kullanıcının bellek bloğu sınırlarının üzerine yazılmadığını doğrulamak için hata ayıklama yığın yordamları tarafından kullanılan bilinen bayt değeriyle doldurulur. Hata ayıklama yığını, yeni bellek bloklarını da bilinen bir değerle doldurur. Serbest blokları yığının bağlı listesinde tutmayı seçerseniz, bu serbest bloklar da bilinen bir değerle doldurulur. Şu anda, kullanılan gerçek bayt değerleri aşağıdaki gibidir:

no_mans_land (0xFD)
Bir uygulama tarafından kullanılan belleğin her iki tarafındaki "no_mans_land" arabellekleri şu anda 0xFD ile doldurulur.

Serbest bloklar (0xDD)
Bayrağı ayarlandığında serbest durumdaki bloklar hata ayıklama yığınının _CRTDBG_DELAY_FREE_MEM_DF bağlı listesinde kullanılmayan durumda 0xDD.

Yeni nesneler (0xCD)
Yeni nesneler ayrıldığında 0xCD ile doldurulur.

Hata ayıklama yığınındaki blok türleri

Hata ayıklama yığınındaki her bellek bloğu beş ayırma türünden birine atanır. Bu türler, sızıntı algılama ve durum raporlama amacıyla farklı şekilde izlenir ve raporlanır. Bir bloğun türünü, gibi _malloc_dbghata ayıklama yığın ayırma işlevlerinden birine doğrudan bir çağrı kullanarak ayırarak belirtebilirsiniz. Hata ayıklama yığınındaki beş tür bellek bloğu (yapının üyesinde nBlockUse _CrtMemBlockHeader ayarlanır) aşağıdaki gibidir:

_NORMAL_BLOCK
Çağrısı malloc veya calloc Normal bloğu oluşturur. Yalnızca Normal blokları kullanmayı planlıyorsanız ve İstemci bloklarına ihtiyacınız yoksa, tanımlamak _CRTDBG_MAP_ALLOCisteyebilirsiniz. _CRTDBG_MAP_ALLOC tüm yığın ayırma çağrılarının Hata ayıklama derlemelerindeki hata ayıklama eşdeğerleriyle eşlenmesine neden olur. İlgili blok üst bilgisinde her ayırma çağrısıyla ilgili dosya adı ve satır numarası bilgilerinin depolanmasına izin verir.

_CRT_BLOCK
Birçok çalışma zamanı kitaplığı işlevi tarafından dahili olarak ayrılan bellek blokları, ayrı ayrı işlenebilmeleri için CRT blokları olarak işaretlenir. Sonuç olarak, sızıntı algılama ve diğer işlemler bunlardan etkilenmeyebilir. Ayırma hiçbir zaman herhangi bir CRT türü bloğunu ayırmamalı, yeniden ayırmamalı veya serbest bırakmamalıdır.

_CLIENT_BLOCK
Uygulama, hata ayıklama yığın işlevlerine yönelik açık çağrıları kullanarak bu tür bellek bloğu olarak ayırarak belirli bir ayırma grubunu hata ayıklama amacıyla özel olarak izleyebilir. Örneğin MFC, tüm CObject nesneleri İstemci blokları olarak ayırır; diğer uygulamalar İstemci bloklarında farklı bellek nesneleri tutabilir. daha fazla izleme ayrıntı düzeyi için İstemci bloklarının alt türleri de belirtilebilir. İstemci bloklarının alt türlerini belirtmek için, sayıyı 16 bit sola ve OR ile birlikte _CLIENT_BLOCKkaydırın. Örneğin:

#define MYSUBTYPE 4
freedbg(pbData, _CLIENT_BLOCK|(MYSUBTYPE<<16));

İstemci bloklarında depolanan nesnelerin dökümünü almak için istemci tarafından sağlanan bir kanca işlevi kullanılarak _CrtSetDumpClientyüklenebilir ve bir İstemci bloğu bir hata ayıklama işlevi tarafından her bırakıldığında çağrılır. Ayrıca, _CrtDoForAllClientObjects hata ayıklama yığınındaki her İstemci bloğu için uygulama tarafından sağlanan belirli bir işlevi çağırmak için de kullanılabilir.

_FREE_BLOCK
Normalde, serbest olan bloklar listeden kaldırılır. Boş belleğin yazılıp yazılmadığını denetlemek veya düşük bellek koşullarının benzetimini yapmak için, serbest blokları bağlı listede, Ücretsiz olarak işaretlenebilir ve bilinen bir bayt değeriyle (şu anda 0xDD) doldurabilirsiniz.

_IGNORE_BLOCK
Hata ayıklama yığını işlemlerini belirli bir aralıkta kapatmak mümkündür. Bu süre boyunca bellek blokları listede tutulur, ancak Yoksay blokları olarak işaretlenir.

Belirli bir bloğun türünü ve alt türünü belirlemek için işlevini _CrtReportBlockType ve makrolarını _BLOCK_TYPE ve _BLOCK_SUBTYPEkullanın. Makrolar içinde <crtdbg.h> aşağıdaki gibi tanımlanır:

#define _BLOCK_TYPE(block)          (block & 0xFFFF)
#define _BLOCK_SUBTYPE(block)       (block >> 16 & 0xFFFF)

Yığın bütünlüğünü ve bellek sızıntılarını denetleme

Hata ayıklama yığınının özelliklerinin çoğuna kodunuzun içinden erişilmelidir. Aşağıdaki bölümde bazı özellikler ve bunların nasıl kullanılacağı açıklanmaktadır.

_CrtCheckMemory
Örneğin, herhangi bir noktada yığının bütünlüğünü denetlemek için çağrısı _CrtCheckMemorykullanabilirsiniz. Bu işlev yığındaki tüm bellek bloklarını inceler. Bellek bloğu üst bilgisi bilgilerinin geçerli olduğunu doğrular ve arabelleklerin değiştirilmediğini onaylar.

_CrtSetDbgFlag
Hata ayıklama yığınının, işlevi kullanılarak _CrtSetDbgFlag okunabilen ve ayarlanabilen bir iç bayrağı _crtDbgFlagkullanarak ayırmaları nasıl takip ettiğini denetleyebilirsiniz. Bu bayrağı değiştirerek, hata ayıklama yığınına program çıktığında bellek sızıntılarını denetlemesini ve algılanan sızıntıları bildirmesini bildirebilirsiniz. Benzer şekilde, düşük bellek durumlarının benzetimini yapmak için yığına bağlı listede boş bellek blokları bırakmasını söyleyebilirsiniz. Yığın kontrol edildiğinde, bu serbest bırakılmış bloklar rahatsız edildiklerinden emin olmak için tamamen incelenir.

_crtDbgFlag bayrağı aşağıdaki bit alanlarını içerir:

Bit alanı Varsayılan değer Açıklama
_CRTDBG_ALLOC_MEM_DF Açık Hata ayıklama ayırmayı açar. Bu bit kapalı olduğunda, ayırmalar zincirlenmiş olarak kalır, ancak blok türleri olur _IGNORE_BLOCK.
_CRTDBG_DELAY_FREE_MEM_DF Kapalı Düşük bellek koşullarının benzetiminden dolayı belleğin gerçekten boşaltılmasını engeller. Bu bit açık olduğunda, serbestleştirilmiş bloklar hata ayıklama yığınının bağlı listesinde tutulur, ancak olarak _FREE_BLOCK işaretlenir ve özel bir bayt değeriyle doldurulur.
_CRTDBG_CHECK_ALWAYS_DF Kapalı Her ayırmada ve serbest bırakmada çağrılmaya neden olur _CrtCheckMemory . Yürütme daha yavaştır, ancak hataları hızla yakalar.
_CRTDBG_CHECK_CRT_DF Kapalı Tür _CRT_BLOCK olarak işaretlenmiş blokların sızıntı algılama ve durum farkı işlemlerine dahil edilmesine neden olur. Bu bit kapalı olduğunda, çalışma zamanı kitaplığı tarafından dahili olarak kullanılan bellek bu tür işlemler sırasında yoksayılır.
_CRTDBG_LEAK_CHECK_DF Kapalı Program çıkışında bir çağrısı aracılığıyla sızıntı denetiminin gerçekleştirilecek olmasına _CrtDumpMemoryLeaksneden olur. Uygulama ayırdığı tüm belleği boşaltamadıysa bir hata raporu oluşturulur.

Hata ayıklama yığınını yapılandırma

, , , , realloc, ve gibi mallocyığın işlevlerine yapılan tüm çağrılar, delete hata ayıklama yığınında çalışan bu işlevlerin sürümlerinde hata ayıklamak için çözümler. newcallocfree Bir bellek bloğunu serbest bırakırsanız, hata ayıklama yığını ayrılmış alanınızın her iki tarafındaki arabelleklerin bütünlüğünü otomatik olarak denetler ve üzerine yazma işlemi gerçekleşirse bir hata raporu oluşturur.

Hata ayıklama yığınını kullanmak için

  • Uygulamanızın hata ayıklama derlemesini C çalışma zamanı kitaplığının hata ayıklama sürümüyle bağlayın.

Bir veya daha fazla _crtDbgFlag bit alanını değiştirmek ve bayrak için yeni bir durum oluşturmak için

  1. parametresi (newFlaggeçerli _crtDbgFlag durumu elde etmek için) olarak ayarlanmış _CRTDBG_REPORT_FLAG olarak çağırın _CrtSetDbgFlag ve döndürülen değeri geçici bir değişkende depolayın.

  2. Karşılık gelen bit maskeleriyle geçici değişkende bit düzeyinde | işleç ("veya") kullanarak herhangi bir biti açın (bildirim sabitleriyle uygulama kodunda gösterilir).

  3. Uygun bit maskelerinin bit düzeyinde & işleci ("değil" veya tamamlayıcısı) olan değişkende bit düzeyinde ~ işleç ("ve") kullanarak diğer bitleri kapatın.

  4. için _crtDbgFlagyeni durumu oluşturmak için geçici değişkende depolanan değere ayarlanmış parametresiyle newFlag çağrısında _CrtSetDbgFlag bulunur.

    Örneğin, aşağıdaki kod satırları otomatik sızıntı algılamayı etkinleştirir ve türündeki _CRT_BLOCKbloklar için denetimleri devre dışı bırakır:

    // Get current flag
    int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
    
    // Turn on leak-checking bit.
    tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
    
    // Turn off CRT block checking bit.
    tmpFlag &= ~_CRTDBG_CHECK_CRT_DF;
    
    // Set flag to the new value.
    _CrtSetDbgFlag( tmpFlag );
    

new, deleteve _CLIENT_BLOCK C++ hata ayıklama yığınındaki ayırmalar

C çalışma zamanı kitaplığının hata ayıklama sürümleri, C++ new ve delete işleçlerinin hata ayıklama sürümlerini içerir. Ayırma türünü kullanırsanız _CLIENT_BLOCK , aşağıdaki örnekte gösterildiği gibi doğrudan işlecin hata ayıklama sürümünü new çağırmanız veya hata ayıklama modunda işlecini new değiştiren makrolar oluşturmanız gerekir:

/* MyDbgNew.h
 Defines global operator new to allocate from
 client blocks
*/

#ifdef _DEBUG
   #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
   #define DEBUG_CLIENTBLOCK
#endif // _DEBUG

/* MyApp.cpp
        Use a default workspace for a Console Application to
 *      build a Debug version of this code
*/

#include "crtdbg.h"
#include "mydbgnew.h"

#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif

int main( )   {
    char *p1;
    p1 =  new char[40];
    _CrtMemDumpAllObjectsSince( NULL );
}

İşlecin Hata Ayıklama sürümü delete tüm blok türleriyle çalışır ve yayın sürümünü derlerken programınızda hiçbir değişiklik gerektirmez.

Yığın durumu raporlama işlevleri

Belirli bir zamanda yığının durumunun özet anlık görüntüsünü yakalamak için içinde <crtdbg.h>tanımlanan yapıyı _CrtMemState kullanın:

typedef struct _CrtMemState
{
    // Pointer to the most recently allocated block:
    struct _CrtMemBlockHeader * pBlockHeader;
    // A counter for each of the 5 types of block:
    size_t lCounts[_MAX_BLOCKS];
    // Total bytes allocated in each block type:
    size_t lSizes[_MAX_BLOCKS];
    // The most bytes allocated at a time up to now:
    size_t lHighWaterCount;
    // The total bytes allocated at present:
    size_t lTotalCount;
} _CrtMemState;

Bu yapı, hata ayıklama yığınının bağlı listesindeki ilk (en son ayrılan) bloğuna bir işaretçi kaydeder. Ardından, iki dizide, her bellek bloğu türünden (_NORMAL_BLOCK, _CLIENT_BLOCK, _FREE_BLOCKvb.) kaç tane olduğunu ve her blok türünde ayrılan bayt sayısını kaydeder. Son olarak, yığında o noktaya kadar bir bütün olarak ayrılan en yüksek bayt sayısını ve şu anda ayrılan bayt sayısını kaydeder.

Diğer CRT raporlama işlevleri

Aşağıdaki işlevler yığının durumunu ve içeriğini bildirir ve bellek sızıntılarını ve diğer sorunları algılamaya yardımcı olmak için bu bilgileri kullanır.

İşlev Açıklama
_CrtMemCheckpoint Yığının anlık görüntüsünü uygulama tarafından sağlanan bir _CrtMemState yapıya kaydeder.
_CrtMemDifference İki bellek durumu yapısını karşılaştırır, aralarındaki farkı üçüncü bir durum yapısında kaydeder ve iki durum farklıysa TRUE döndürür.
_CrtMemDumpStatistics Belirli _CrtMemState bir yapıyı döküm eder. Yapı, belirli bir anda hata ayıklama yığını durumunun anlık görüntüsünü veya iki anlık görüntü arasındaki farkı içerebilir.
_CrtMemDumpAllObjectsSince Yığının belirli bir anlık görüntüsü alındığından veya yürütmenin başlangıcından itibaren ayrılan tüm nesneler hakkındaki bilgileri dökümler. Bir _CLIENT_BLOCK bloğun dökümünü her atışında, kullanılarak _CrtSetDumpClientyüklenmişse uygulama tarafından sağlanan bir kanca işlevini çağırır.
_CrtDumpMemoryLeaks Program yürütmenin başlangıcından bu yana bellek sızıntıları oluşup oluşmadığını belirler ve varsa, ayrılan tüm nesnelerin dökümünü alır. Bir _CLIENT_BLOCK bloğun dökümünü her atıldığında_CrtDumpMemoryLeaks, kullanılarak _CrtSetDumpClientyüklenmişse uygulama tarafından sağlanan bir kanca işlevini çağırır.

Yığın ayırma isteklerini izleme

Bir onay veya raporlama makrosunun kaynak dosya adını ve satır numarasını bilmek genellikle sorunun nedenini bulmada yararlıdır. Aynı değerin yığın ayırma işlevleri için de geçerli olma olasılığı yoktur. Bir uygulamanın mantıksal ağacına birçok uygun noktaya makro ekleyebilmenize karşın, ayırma genellikle birçok farklı zamanda birçok farklı yerden çağrılan bir işleve gömülür. Soru, hangi kod satırının hatalı ayırma yaptığı değildir. Bunun yerine, bu kod satırı tarafından yapılan binlerce ayırmadan hangisinin hatalı olduğu ve neden olduğuydu.

Benzersiz ayırma isteği numaraları ve _crtBreakAlloc

Kötü giden belirli yığın ayırma çağrısını tanımlamanın basit bir yolu vardır. Hata ayıklama yığınındaki her blokla ilişkili benzersiz ayırma isteği numarasından yararlanır. Bir blok hakkındaki bilgiler döküm işlevlerinden biri tarafından bildirildiğinde, bu ayırma isteği numarası ayraç içine alınır (örneğin, "{36}").

Hatalı ayrılmış bir bloğun ayırma isteği numarasını bildiğinizde, kesme noktası oluşturmak için bu sayıyı adresine _CrtSetBreakAlloc geçirebilirsiniz. Yürütme, bloğu ayırmadan hemen önce bozulacak ve hatalı çağrıdan hangi yordamın sorumlu olduğunu belirlemek için geri gidebilirsiniz. Yeniden derlemeyi önlemek için, ilgilendiğiniz ayırma isteği numarasını ayarlayarak _crtBreakAlloc hata ayıklayıcıda aynı işlemi gerçekleştirebilirsiniz.

Ayırma yordamlarınızın hata ayıklama sürümlerini oluşturma

Daha karmaşık bir yaklaşım, yığın ayırma işlevlerinin sürümlerine benzer _dbg şekilde kendi ayırma yordamlarınızın Hata Ayıklama sürümlerini oluşturmaktır. Ardından kaynak dosya ve satır numarası bağımsız değişkenlerini temel alınan yığın ayırma yordamlarına geçirebilirsiniz ve hatalı ayırmanın nereden kaynaklandığını hemen görebilirsiniz.

Örneğin, uygulamanızın aşağıdaki örneğe benzer yaygın olarak kullanılan bir yordam içerdiğini varsayalım:

int addNewRecord(struct RecStruct * prevRecord,
                 int recType, int recAccess)
{
    // ...code omitted through actual allocation...
    if ((newRec = malloc(recSize)) == NULL)
    // ... rest of routine omitted too ...
}

Üst bilgi dosyasına aşağıdaki örnek gibi bir kod ekleyebilirsiniz:

#ifdef _DEBUG
#define  addNewRecord(p, t, a) \
            addNewRecord(p, t, a, __FILE__, __LINE__)
#endif

Ardından, kayıt oluşturma yordamınızdaki ayırmayı aşağıdaki gibi değiştirebilirsiniz:

int addNewRecord(struct RecStruct *prevRecord,
                int recType, int recAccess
#ifdef _DEBUG
               , const char *srcFile, int srcLine
#endif
    )
{
    /* ... code omitted through actual allocation ... */
    if ((newRec = _malloc_dbg(recSize, _NORMAL_BLOCK,
            srcFile, scrLine)) == NULL)
    /* ... rest of routine omitted too ... */
}

Artık çağrılan kaynak dosya adı ve satır numarası addNewRecord , hata ayıklama yığınında ayrılan her sonuçta elde edilen blokta depolanır ve bu blok incelendiğinde bildirilir.

Ayrıca bkz.

Yerel Kodda Hata Ayıklama