9 Ocak 2018 Salı

valgrind

valgrind
Valgrind iki temel görevi yerine getirir.

1. Memory Error'ları bulur. Örneğin Invalid read/write hataları
2. Memory Leak'leri bulur.

Valgrind Niçin Yavaştır
Memory leak bulan basit uygulamalar sadece heap'i takip ederler. Uygulama sonlanırken eğer heap'te halen silinmemiş bellek alanı varsa, bunları yazarlar. Bazı daha gelişmiş olanlar call stack'i de saklayarak kodun akışını da gösterebilirler. Ancak bu tür uygulamalar bellek alanını aşan read/write işlemlerini bulamazlar. valgrind kodu değil ancak instruction'ları instrumente eder. Yani kendi içinde bir virtual machine inşa ederek tüm bellek işlemlerini takip eder. Bu yüzden uygulama daha yavaş açılır ve çalışır.
valgrind works by adding its own instrumentation to your code and then running in on its own virtual machine x86 (or ppc) core. Significantly slows down execution, but still fine for testing.
num-callers seçeneği
num-callers seçeneğini kullanırsak, ilklendirilmemiş değişkeni takip etmek için kaç metod çağrısının kullanılacağını belirtiriz. Şöyle yaparız.
valgrind --leak-check=full --num-callers=20 --tool=memcheck ./program
track-origins seçeneği
valgrind track-origins seçeneği yazısına taşıdım.

--tool seçeneği
Hangi aracın kullanılacağını belirtir. memcheck, cachegrind, callgrind, helgrind, drd, massif, lackey, none, exp-sgcheck, exp-bbv, exp-dhat gibi seçeneklerden birisini alır. Varsayılan araç memcheck olduğu için genelde --tool seçeneği kullanılmaz.

Şöyle yaparız.
valgrind --tool=helgrind ./a.out

mem-check seçeneği
valgrind mem-check aracı yazına taşıdım

leak-check seçeneği
valgrind leak-check aracı yazısına taşıdım.

-v seçeneği
Bu seçeneği anlamadım ancak sanırım verbose anlamına geliyor.

Hata satırları

1. invalid read ne demek
invalid read Hatası yazısına taşıdım.

2. invalid write ne demek
malloc veya new ile ayrılan bellek alanının dışına taşıldığını yada zaten silinmiş bir belleğin tekrar silindiğini belirtir. by ile başlayan satırdan itibaren hatanın nerede oluştuğu takip edilebilir. at ile başlayan satır hatanın tam yerini belirtir.
==3526== Invalid write of size 8
==3526==    at 0x403554: ChessPiece::~ChessPiece() (ChessPiece.h:13)
==3526==    by 0x404F43: Cell::~Cell() (Cell.cpp:14)
==3526==    by 0x401CBC: void std::_Destroy<Cell>(Cell*)(stl_construct.h:93)
3. invalid free ne demek
Şöyle bir hata alırız.
==2642== Invalid free() / delete / delete[] / realloc()
...
Elimizde bir dizi olsun
int_array=new T[10];
Bu diziyi şöyle silmek yerine
delete[] int_array;
şöyle silersek bu hatayı alırız.
delete int_array;
4. Uninitialized ne demek
Şöyle çeşitleri var.
1. Uninitialised value was created by a stack allocation
2. Uninitialised value was created by a heap allocation
3. Conditional jump or move depends on uninitialised value(s)

Uninitialised value was created by a stack allocation nedir
Bir bellek alanına değer atanması beklenirken, atanmaması anlamına geliyor. Örnek'te sscanf 3'ten daha az parametre okuduğu için valgrind bu uyarıyı veriyor.
int dd,mm,yy;
sscanf(cdate.c_str(),"%2d%2d%4d",&mm,&dd,&yy);
Uninitialised value was created by a heap allocation
Örnek ver

Conditional jump or move depends on uninitialised value(s) nedir
valgrind Conditional jump.. Hatası yazısına taşıdım.

Heap Summary
Raporun en sonunda şuna benzer bir özet görürüz.
==6254== HEAP SUMMARY:
==6254==     in use at exit: 999 bytes in 173 blocks
==6254==   total heap usage: 181 allocs, 8 frees, 5,631 bytes allocated
==6254== 
==6254== 999 bytes in 173 blocks are definitely lost in loss record 1 of 1
==6254==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/...)
==6254==    by 0x4008A5: main (readin-test.c:25)
Eğer sorun yoksa şuna benzer bir özet görürüz.
==9145== HEAP SUMMARY:
==9145==     in use at exit: 64 bytes in 2 blocks
==9145==   total heap usage: 8 allocs, 6 frees, 300 bytes allocated
==9145== 
==9145== Searching for pointers to 2 not-freed blocks
==9145== Checked 73,312 bytes
Leak Summary
Raporun en sonunda şuna benzer bir özet görürüz.
==6254== LEAK SUMMARY:
==6254==    definitely lost: 999 bytes in 173 blocks
==6254==    indirectly lost: 0 bytes in 0 blocks
==6254==      possibly lost: 0 bytes in 0 blocks
==6254==    still reachable: 0 bytes in 0 blocks
==6254==         suppressed: 0 bytes in 0 blocks
==6254== 
Eğer sorun yoksa şuna benzer bir özet görürüz.
==9145== LEAK SUMMARY:
==9145==    definitely lost: 0 bytes in 0 blocks
==9145==    indirectly lost: 0 bytes in 0 blocks
==9145==      possibly lost: 0 bytes in 0 blocks
==9145==    still reachable: 64 bytes in 2 blocks
==9145==         suppressed: 0 bytes in 0 blocks
==9145== Reachable blocks (those to which a pointer was found) are not shown.
==9145== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==9145== 
==9145== Use --track-origins=yes to see where uninitialised values come from
==9145== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
possibly lost ne demek:
Programdan çıkarken halen kullanımda olan bellek alanlarını kastediyor. Örneğin program çıkarken halen çalışmakta olan bir thread, possibly lost olarak belirtiliyor.

Programdan çıkarken kaynakları temizleyip temizlememek konusunda insanlar ikiye ayrılmış durumdalar. İlk grup nasıl olsa işletim sistemi temizler diye hiç bir şeyi temizlemiyor. Hatta temizlik işlemi çok vakit alıyor diyen de var. Bu tür yazılımlarda valgrind bir sürü hata veriyor. Bu yüzden gerçek hataları arayıp bulmak zahmetli oluyor.

Diğer grup ise her şeyi düzgünce kapatıp temizleme taraftarı. Bu durum ise fazladan kodlama ve çaba gerektiriyor.

Sadece valgrind ile çalışırken belleği temizlemeyi sağlayan bir seçenek eklemek iyi bir çözüm olurdu ancak yok:)

Visual Studio
Visual Studio ile invalid write yani buffer overflow işlemlerini tespit etmek için 3 yöntem var.
Bunlar
1.CRT Debug Heap
2. Application Verifier
3. /GS veya /sdl derleme seçenekleri

1.CRT Debug Heap
Şu satırı dahil ederiz.
#include <crtdbg.h>
CRT Debug Heap için aşağıdaki metodlar kullanılabilir.

_CrtSetDbgFlag metodu
 _CrtSetDbgFlag() metoduna bir çok parametre geçilebiliyor. Hepsi ne işe yarıyor bilmiyorum.

_CRTDBG_ALLOC_MEM_DF 
Açıklaması şöyle. Bellek isteklerini takip etmeyi başlatır.
_CRTDBG_ALLOC_MEM_DF flag is used to turn allocation tracking on on off.
_CRTDBG_DELAY_FREE_MEM_DF
Açıklaması şöyle. Dangling pointer'ı (free/delete edilmiş belleğe pointer) bulmak için kullanılır.
When you call delete/free, the CRT will set the block it requested from HeapAlloc() to 0xDD, indicating this is a free zone. Normally after this, free() will call HeapFree() to give back the block to the Win32 heap, in which case the block will be overwritten with 0xFEEEEEEE, to indicate Win32 heap free memory.

You can avoid this by using the CRTDBG_DELAY_FREE_MEM_DF flag to _CrtSetDbgFlag(). It prevents memory from actually being freed, as for simulating low-memory conditions. When this bit is on, freed blocks are kept in the debug heap's linked list but are marked as _FREE_BLOCK. This is useful if you want to detect dangling pointers errors, which can be done by verifying if the freed block is written with 0xDD pattern or something else. Use _CrtCheckMemory() to verify the heap.s integrity.
_CRTDBG_LEAK_CHECK_DF
Açıklaması şöyle.
_CRTDBG_LEAK_CHECK_DF will cause memory leak checking to be performed on application exit.
Örnek
Şöyle yaparız.
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF |
 _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_CHECK_CRT_DF |
 _CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_CHECK_EVERY_16_DF);
Örnek
Benim kullandığım şöyle.

#include <crtdbg.h>
_CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF     || //Verify heap
                            _CRTDBG_LEAK_CHECK_DF      //Verify heap leak
                           );
_CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG);

int* array = new int [5];
array [5] = 5; //Buffer overrun
delete [] array;
Output penceresinde : CRT detected that the application wrote to memory after end of heap buffer

Örnek
Şöyle yaparız.
int main(int argc, char* argv[])
{
#if defined(WIN32) || defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS_)
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
  ...

    return 0;
}
Örnek
Şöyle yaparız.
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
_CrtSetReportMode metodu
_CRTDBG_MODE_DEBUG
Açıklaması şöyle.
_CRTDBG_MODE_DEBUG will cause the output to be redirected to a debug window like DebugView or Output Window in Visual Studio.
Şöyle yaparız.
  _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
_CrtDumpMemoryLeaks metodu
_CrtDumpMemoryLeaks() ile bellek leak'leri raporlarnır. Şöyle yaparız.
#include <crtdbg.h>

#define _CRTDBG_MAP_ALLOC

using namespace std;

int main(void){

  _CrtDumpMemoryLeaks();
  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
  ...
  return 0;
}
Output penceresinde şu çıktıyı alırız.
Detected memory leaks!
Dumping objects ->
{143} normal block at 0x007DAE50, 8 bytes long.
 Data: <  B     > 18 FB 42 00 00 00 00 00 
{142} normal block at 0x007DAE08, 8 bytes long.
 Data: << B     > 3C FB 42 00 00 00 00 00 
Ya da şöyle bir çıktı alırız.
Detected memory leaks!
Dumping objects ->
{74} normal block at 0x0000015C0979EFF0, 1 bytes long.
 Data: < > CD 
Object dump complete.
2. Application Verifier
Sanırım sadece Win32 çağrıları ile oluşan hataları tespit ediyor.




Hiç yorum yok:

Yorum Gönder