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_dbg
hata 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_ALLOC
isteyebilirsiniz. _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_BLOCK
kaydı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 _CrtSetDumpClient
yü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_SUBTYPE
kullanı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ı _CrtCheckMemory
kullanabilirsiniz. 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ğı _crtDbgFlag
kullanarak 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 _CrtDumpMemoryLeaks neden 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 malloc
yığı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. new
calloc
free
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
parametresi (
newFlag
geç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.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).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.için
_crtDbgFlag
yeni durumu oluşturmak için geçici değişkende depolanan değere ayarlanmış parametresiylenewFlag
ç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_BLOCK
bloklar 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
, delete
ve _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_BLOCK
vb.) 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 _CrtSetDumpClient yü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 _CrtSetDumpClient yü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.