Valgrind to narzędzie do usuwania problemów związanych z zarządzaniem dynamicznie alokowaną pamięcią. Wczytuje i wykonuje instrukcje analizowane programu, gromadząc przy tym dane potrzebne do wykrycia ewentualnych błędów. Może także działać jako profiler, zbierający informacje o wykorzystaniu pamięci podręcznych procesora, albo program wykrywający typowe problemy w aplikacjach wielowątkowych.
Żeby wykorzystać Valgrind do analizy programu napisanego w Pascalu trzeba podczas kompilacji podać opcję ‘-v’.
$ ppcx64 -gv -O- program.pas
W powyższym przykładzie ‘-O-‘ to nie meksykanin na rowerze widziany z góry, lecz opcja wyłączająca optymalizację.
Przylad uruchomienia programu:
$ ./valgrind --leak-check=full ./program
Ponieważ instrukcje są intepretowane przez Valgrind, a nie wykonywane bezpośrednio przez procesor, należy się spodziewać znacznego zmniejszenia wydajności programu (ok. 20-30x).
Poniżej przedstawiono kilka przykładów błędów, które może wykryć Valgrind.
Poniższy program alokuje dwa bloki pamięci, ale zwalnie tylko jeden (adres pierwszego bloku jest zamazywany w drugim wywyołaniu ‘New’).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | program simpleleak;
{$mode objfpc}{$H+}
procedure Test;
var
Ptr : ^Integer;
begin
New(Ptr);
New(Ptr); { Poprzedni wskaźnik zostanie nadpisany. }
Dispose(Ptr);
end;
begin
Test;
end.
|
Wynik analizy przeprowadzonej przez Valgrind:
tsznuk@grzebien:~/ipp$ ppcx64 -O- -gv simpleleak.pas
tsznuk@grzebien:~/ipp$ valgrind --leak-check=full ./simpleleak
==2490== Memcheck, a memory error detector
==2490== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==2490== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==2490== Command: ./simpleleak
==2490==
==2490==
==2490== HEAP SUMMARY:
==2490== in use at exit: 12 bytes in 1 blocks
==2490== total heap usage: 2 allocs, 1 frees, 24 bytes allocated
==2490==
==2490== 12 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2490== at 0x4C2779D: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2490== by 0x422CF6: CMEM_CGETMEM$QWORD$$POINTER (in /home/tsznuk/ipp/simpleleak)
==2490== by 0x4046AD: main (simpleleak.pas:15)
Jak widać wyciek pamięci został wykryty. Co więcej, Valgrind wydrukował ciąg wywołań, który doprowadził do zaalokowania zgubionego bloku. Co prawda w wydrukowanym śladzie brakuje kluczowego wpisu (funkcji Test i numeru linii: 9), ale musimy mieć na uwadze, że Valgrind i FPC to programy darmowe i dostajemy, za co zapłaciliśmy.
Poniższy program usiłuje dwukrotnie zdealokować ten sam blok pamięci.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | program doublefree;
{$mode objfpc}{$H+}
procedure Test;
var
Ptr : ^Integer;
begin
New(Ptr);
Dispose(Ptr);
Dispose(Ptr); { Podwójne wywołanie. }
end;
begin
Test;
end.
|
Wynik analizy przeprowadzonej przez Valgrind:
tsznuk@grzebien:~/ipp$ ppcx64 -O- -gv doublefree.pas
tsznuk@grzebien:~/ipp$ valgrind --leak-check=full ./doublefree
==2513== Memcheck, a memory error detector
==2513== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==2513== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==2513== Command: ./doublefree
==2513==
==2513== Invalid free() / delete / delete[]
==2513== at 0x4C268FE: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2513== by 0x422D14: CMEM_CFREEMEM$POINTER$$QWORD (in /home/tsznuk/ipp/doublefree)
==2513== by 0x40469D: main (doublefree.pas:15)
==2513== Address 0x51b4040 is 0 bytes inside a block of size 12 free'd
==2513== at 0x4C268FE: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2513== by 0x422D14: CMEM_CFREEMEM$POINTER$$QWORD (in /home/tsznuk/ipp/doublefree)
==2513== by 0x40469D: main (doublefree.pas:15)
Valgrind wykrył podwójną dealokacje i wypisał ślad obu wywołań ‘Dispose’. Niestety niekompletny, tak jak w poprzednim przykładzie.
Program dealokuje blok pamięci, a potem zapisuje do niego wartość.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | program earlyfree;
{$mode objfpc}{$H+}
procedure Test;
var
Ptr : ^Integer;
begin
New(Ptr);
Dispose(Ptr);
Ptr^ := 42; { Pamięć została przed chwilą zwolniona. }
end;
begin
Test;
end.
|
Wynik analizy przeprowadzonej przez Valgrind:
tsznuk@grzebien:~/ipp$ ppcx64 -O- -gv earlyfree.pas
tsznuk@grzebien:~/ipp$ valgrind --leak-check=full ./earlyfree
==2527== Memcheck, a memory error detector
==2527== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==2527== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==2527== Command: ./earlyfree
==2527==
==2527== Invalid write of size 4
==2527== at 0x404688: P$DOUBLEFREE_TEST (earlyfree.pas:11)
==2527== by 0x40469D: main (earlyfree.pas:15)
==2527== Address 0x51b4048 is 8 bytes inside a block of size 12 free'd
==2527== at 0x4C268FE: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2527== by 0x422D14: CMEM_CFREEMEM$POINTER$$QWORD (in /home/tsznuk/ipp/earlyfree)
==2527== by 0x40469D: main (earlyfree.pas:15)
Valgrind odkrył niepoprawny zapis. Wykrył też, że pamięć, którą program usiłował modyfikować, była wcześniej zwolniona - i wypisał również sład prowadzący do wykonania owej dealokacji. Co ciekawe, ślad zapisu zaiwera dokładny i poprawny numer linii.
Przykład klasycznego błędu - odwołanie do dynamicznie zaalokowanej tablicy z przekroczeniem zakresu.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | program overflow;
{$mode objfpc}{$H+}
procedure Test;
const
N = 10;
type
TNumberArray = array [0 .. N - 1] of Integer;
PNumberArray = ^TNumberArray;
var
Numbers : PNumberArray;
i : Integer;
begin
GetMem(Numbers, SizeOf(TNumberArray));
for i := 1 to N do { Powinno być 0 .. N - 1 }
Numbers^[i] := 42;
FreeMem(Numbers);
end;
begin
Test;
end.
|
Wynik analizy przeprowadzonej przez Valgrind:
tsznuk@grzebien:~/ipp$ ppcx64 -O- -gv overflow.pas
tsznuk@grzebien:~/ipp$ valgrind --leak-check=full ./overflow
==2554== Memcheck, a memory error detector
==2554== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==2554== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==2554== Command: ./overflow
==2554==
==2554== Invalid write of size 4
==2554== at 0x404648: P$OVERFLOW_TEST (overflow.pas:17)
==2554== by 0x404675: main (overflow.pas:22)
==2554== Address 0x51b405c is 0 bytes after a block of size 48 alloc'd
==2554== at 0x4C2779D: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2554== by 0x422CB6: CMEM_CGETMEM$QWORD$$POINTER (in /home/tsznuk/ipp/overflow)
==2554== by 0x404675: main (overflow.pas:22)
Valgrind wykrył zapis do niezaalokowanej pamięci. Dodatkowo znalazł najbliższy poprawnie zaalokowany blok, co jest pomocne przy diagnozowaniu błędu.
Free Pascal zawiera własny mechanizm do wykrywania wycieków pamięci - moduł heaptrc. Ma mniejsze możliwości, niż Valgrind, ale jest bardziej wydajny. Żeby z niego skorzystać, trzeba skompilować program z opcją ‘-gh’. Dodanie opcji ‘gl’ pozwoli nam odczytać numery linii z końcowego raportu (który powstanie po uruchomieniu programu).
Przykład
[tsznuk@duch ipptmp]$ ppcx64 -gh -gl simpleleak.pas
[tsznuk@duch ipptmp]$ ./simpleleak
Heap dump by heaptrc unit
2 memory blocks allocated : 8/16
1 memory blocks freed : 4/8
1 unfreed memory blocks : 4
True heap size : 32768
True free heap : 32608
Should be : 32632
Call trace for block $00007F6C539F20C0 size 4
$000000000040022E line 15 of simpleleak.pas
$00000000004001B8
Na użytek zajęć z IPP standardem kodowania nazwiemy zestaw reguł, których stosowanie podczas tworzenia kodu programu służy (przynajmniej w teorii) zwiększeniu czytelności owego kodu. Zakres zagadnień, które opisuje taki standard, zależy od konkretnego projektu. Poniżej przedstawiono przykłady reguł i kwestii, które zwykle reguluje konwencja kodowania.
Ostrzeżenie
Poniższy rozdział zawiera losowe przykłady reguł, pochodzących z różnych standardów. Nie należy traktować podanych tu zasad jako spójnej konwencji kodowania.
Todo
Uzupełnić
Konwencje opracowane przez Borland/Inprise/CodeGear/Embarcadero i rozwinięte przez projekt JEDI:
http://wiki.delphi-jedi.org/index.php?title=Style_Guide
Zestaw reguł i uwag z dokumentacji kompilatora GPC:
http://www.gnu-pascal.de/h-gpcs-en.html
Jeszcze jeden standard (Delphi 4 Developer’s Guide)