Biuletyn nr 26

Biuletyn KDM
1 | 2 | 3 | 4 | 5
6 | 7 | 8 | 9 | 10
11 | 12 | 13 | 14
15 | 16 | 17 | 18
19 | 20 | 21 | 22
23 | 24 | 25 | 26
27 | 28 | 29 | 30
31 | 32
Lista biuletynów

Biuletyn nr 26 (31 stycznia 2008)

Spis treści

Porady: Wyrażaj się regularnie!

Autor: Michał Łopuszyński

Do czego służą wyrażenia regularne?

Wyrażenia regularne (ang. regular expressions, czasem skrótowo zwane regex,regexp) są sposobem opisu wzorców ciągów znakowych. Bardzo często przydają się one w zagadnieniach związanych z przeszukiwaniem zbiorów tekstowych np. mogą pomóc nam znaleźć specyficzny fragment w kodzie źródłowym programu, wyciągnąć potrzebną informację z obszernych plików wynikowych naszego ulubionego narzędzia obliczeniowego itp. Można je wykorzystać w pracy z wieloma standardowymi narzędziami uniksowymi takimi, jak grep, sed, vi(m) itd. Większość nowoczesnych języków programowania standardowo dysponuje narzędziami do obsługi wyrażeń regularnych, przykładami mogą tu być np. Python, Java, Perl itp.

Różne przykłady

Zamiast teorii przejdźmy od razu do przykładów zastosowania wyrażeń regularnych w pracy z programem grep, czyli standardowym narzędziem uniksowym do przeszukiwania tekstu. Podstawowa składnia polecenia grep jest następująca:

 grep 'reg_exp' tekst.txt

Wynikiem takiego wywołania będą wszystkie linie pliku tekst.txt pasujące do szablonu opisanego przez regularne wyrażenie 'reg_exp'.

 

Przykład 0. Zobaczmy jak to działa:

 grep 'Ala' tekst.txt

wypisze nam wszystkie linijki pliku tekst.txt zawierające ciąg Ala.

 

Przykład 1. Wypisywanie linijek zawierających podany tekst to nic wielkiego. A gdybyśmy za jednym zamachem chcieli wypisać wszystkie linie zawierające ciągi Ela, Ola bądź Ula? Proszę bardzo:

 grep '[EOU]la' tekst.txt

Wyjaśnienie: W nawiasach kwadratowych podajemy zestaw znaków, który może pojawić się na danym miejscu. Można też użyć zakresu tzn. zamiast pisać [abcdef] wystarczy [a-f].

 

Przykład 2. Teraz jeszcze jedna wariacja na temat możliwości jakie dają symbole []. Często zamiast wymieniać znaki, które mogą wystąpić na danej pozycji, wygodniej jest podać znaki, których na pewno nie chcemy na danej pozycji znaleźć:

 grep 'El[^a]' tekst.txt

wyszuka nam wszystkie łańcuchy rozpoczynające się od El, za wyjątkiem wyrazu Ela, np. Eli, Elo, Elu, Elx itp.

 

Przykład 3. No dobrze, to utrudnijmy jeszcze - chcielibyśmy, aby poszukiwany ciąg znaków "Ela", koniecznie znajdował się zaraz na początku wiersza. Takie wyszukiwanie realizuje polecenie poniżej:

 grep '^Ela' tekst.txt

Wyjaśnienie: Tym razem symbol ^ (bez [] !) to umowny znak rozpoczęcia linii, analogicznie symbol $ oznacza koniec linii. W przypadku, kiedy w naszym wzorcu chcemy po prostu wyszukać znak ^ stosujemy zapis \^.

 

Przykład 4. Korzystając z wiadomości zebranych powyżej spróbujmy wyszukać tylko linijki zawierające dokładnie 4 znaki. Zrobi to dla nas polecenie:

 grep '^....$' tekst.txt

Wyjaśnienie: Oprócz omówionych powyżej symboli początku i końca linii wykorzystaliśmy symbol ., który oznacza dowolny znak.

 

Przykład 5. A gdybyśmy chcieli zbudować wyrażenie, które oprócz imion Ela, Ola i Ula, wyszuka także ciągi odpowiadające zdrobnieniom Elka, Olka i Ulka. Nic prostszego:

 grep '[EOU]lk*a' tekst.txt

Wyjaśnienie: Symbol * oznacza zero lub więcej wystąpień poprzedzającego go symbolu. Minusem powyższego wyrażenia jest to, że pasuje ono także do łańcuchów zawierających wielokrotne powtórzenia litery k (np. Olkkkka). Poniżej pokażemy jak pozbyć się tej wady.

 

Przykład 6. No właśnie, a jeśli chcemy, żeby jakiś znak pojawiał się w poszukiwanym przez nas ciągu jakąś konkretną liczbę razy? Z pomocą przychodzą nam symbole {}. Przyjrzyjmy się poleceniu, które wyszuka nam wszystkie imiona i zdrobnienia, które chcieliśmy wyszukać w przykładzie 5, ale nie będzie już wrażliwe na ciągi typu Olkkka:

   grep -E '[EOU]lk{0,1}a' tekst.txt

Wyjaśnienie: Symbol {min,max} oznacza, że poprzedzający znak musi pojawić się od min do max razy, czyli powyżej zażądaliśmy 0 lub 1 literki k. Warto zauważyć, że aby polecenie grep rozumiało symbole {} trzeba włączyć obsługę tak zwanych rozszerzonych wyrażeń regularnych (ang. extended regular expressions), co robi przełącznik -E. Inną metodą jest skorzystanie z polecenia egrep zamiast grep.

 

Przykład 7. Przydatną funkcją jest też możliwość podania kilku alternatywnych wzorców. Np. aby wyszukać wszystkie linie zawierające ciąg Ala lub ciąg Azor piszemy:

  grep -E 'Ala|Azor' tekst.txt

Wyjaśnienie: Użyliśmy znaku |, który oznacza alternatywę. Oczywiście można go wykorzystać kilka razy w jednym wyrażeniu. Warto też zauważyć, że znów konieczna jest obsługa rozszerzonych wyrażeń regularnych (opcja -E).

 

Przykład 8. Oprócz -E w praktyce często przydają się także inne przełączniki dla polecenia grep, np.:

  • -i - (ignore case) dopasowuje wyrażenia ignorując wielkość liter,
  • -v - (invert match) wypisuje tylko linie, które nie pasują do wzorca,
  • -r - (recursive) przeszukuje też podkatalogi,
  • -A num lub -B - num - oprócz lini pasującej do wzorca wyświetla num następnych (after) lub poprzednich (before),
  • -C num - oprócz lini pasującej do wzorca wyświetla num poprzednich i num następnych linii (context).

Gdzie szukać dalszych informacji?

W powyższym artykule z pewnością nie wyczerpaliśmy obszernego tematu, jakim są wyrażenia regularne. Więcej informacji na ten temat można znaleźć w zasobach internetowych, np. w:

Narzędzia: Valgrind - Memcheck - czyli memory debugging

Autor: Maciek Cytowski

Co to jest Valgrind?

Valgrind logo.png

Program Valgrind zrodził się z pomysłu napisania darmowego memory debuggera dla Linuksowych architektur x86. Jego szybki rozwój sprawił jednak, iż w tej chwili jest on podstawowym narzędziem dynamicznej analizy programów. Można przy jego użyciu dokonywać między innymi następujących czynności diagnostycznych: debuggowanie, profiling, memory debugging, cache profiling. Lista programów, które zyskały na zetknięciu z Valgrindem jest dosyć długa, a wśród niej znajdziemy takie giganty jak produkty Mozilla Firefox czy Thunderbird. Listę najbardziej spektakularnych projektów korzystających z Valgrinda znajdziemy tutaj: http://valgrind.org/gallery/users.html.

Bardzo wygodną własnością Valgrinda jest fakt, iż pracuje on bezpośrednio na wykonywalnych plikach binarnych naszego programu. Oryginalny program uruchomiony pod kontrolą Valgrinda jest wpierw wczytywany i tłumaczony do tymczasowej, prostszej formy a następnie odpowiednio przetwarzany przez wybrane narzędzia (wymienione w nastepnym rozdziale). Dopiero takie "zmienione" binarki są uruchamiane na procesorze. Większość zmian wprowadzanych przez narzędzia Valgrinda powoduje jednak, że czas wykonania zwiększa się znacznie, niekiedy nawet 4-5 krotnie. Dużą zaletą jest jednak to, iż aby skorzystać z Valgrinda nie musimy modyfikować, rekompilować czy relinkować naszego programu. Oczywiście przy korzystaniu z niektórych narzędzi Valgrinda przyda nam się jednak opcja kompilacji -g.

Większość narzędzi Valgrind zostało przystosowanych do wydajnej pracy z programami napisanymi w językach C oraz C++, jednak jak przyznają twórcy pakietu, były one również testowane na językach takich jak Java, Fortran, Perl, Python.

Kiedy przyda nam się Valgrind?

Tak naprawdę Valgrind przyda nam się zawsze, gdy próbujemy napisać nasz własny kod lub znaleźć błąd w czyimś programie. Przyda nam się on zwłaszcza wtedy gdy program ten napisany jest w języku C lub C++.

Co ciekawe, Valgrind przyda się nam nawet wówczas gdy nasz program nie ma błędów. Dotyczy to sytuacji, gdy program błędów nie ma, podczas gdy jednak je ma. Takie sytuacje określa się zwykle mianem hajsenbug-ów (http://pl.wikipedia.org/wiki/Heisenbug).

Valgrind przyda się również wówczas gdy nie jesteśmy zadowoleni z wydajności naszego kodu.

Narzędzia Valgrind

Valgrind to zestaw kilku narzędzi:

  • Memcheck - sprawdza wszystkie zapisy i odczyty pamięci, śledzi wszystkie wywołania malloc, new, free, delete
  • Cachegrind - narzędzie służące do profilingu wykorzystania pamięci cache, monitoruje tzw. cache misses, może pomóc znacznie poprawić wydajność kodu
  • Callgrind - rozszerzenie programu Cachegrind, posiada tyle informacji co Cachegrind i dodatkowo prezentuje drzewo wywołań programu (narzędzie to było opisane przez Łukasza Ligowskiego - Biuletyn nr 9)
  • Massif - program monitorujący stos naszego programu, prezentacja wizualna wielkości stosu względem czasu pozwala zidentyfikować miejsca w kodzie, które najbardziej absorbują pamięć
  • Helgrind - służy do debugowania programów napisanych przy wykorzystaniu biblioteki pthreads, szuka miejsc w pamięci, które są w danej chwili używane przez więcej niż jeden wątek, a które nie są opatrzone odpowiednim wywołaniem pthread_mutex_

W tym artykule przyjrzymy się dokładniej narzędziu Memcheck. Poznamy niektóre jego opcje i zobaczymy go w akcji na konkretnych przykładach.

Uruchamianie Memcheck'a

Valgrinda uruchamiamy poleceniem:

valgrind -tool=nazwa [opcje Valgrind] program [opcje programu]

gdzie nazwa to oznaczenie narzędzia Valgrinda. Domyślnie używany jest Memcheck.


Dodatkowe opcje:

  • --leak-check=[yes/no] - szukanie tzw. "memory leaks"
  • ---show-reachable=[yes|no] - dodatkowo pokazuje bloki pamięci, które są jeszcze dostępne (nie straciliśmy ich adresu)
  • --log-file=<filename> - wszelkie komunikaty Valgrinda wysyłane do pliku
  • inne: man valgrind

Memcheck w przykładach

Jakie błedy potrafi znaleźć Memcheck:

  • korzystanie z niezainicjalizowanych miejsc pamięci
  • zapisywanie/wczytywanie miejsc w pamięci po ich zwolnieniu
  • zapisywanie/wczytywaniu miejsc pamięci poza końcem zaalokowanego bloku
  • "memory leaks" - zaalokowane miejsca w pamięci, które są trwale tracone
  • błędne użycie malloc, new, new[] oraz free, delete, delete[]

Na kilku poniższych przykładach postaram się zaprezentować jak bardzo pomocnym narzędziem potrafi być Valgrind.


Przykład 1 - zapis poza zaalokowanym blokiem pamięci

Przyjrzyjmy się następującemu przykładowi:

 1 #include <stdlib.h>
 2 #define N 101
 3
 4 int main()
 5 {
 6         int index;
 7         int *table;
 8
 9         table=(int*) malloc(N*sizeof(int));
10
11         for(index=0;index<N+1;index++)
12                 table[index]=index;
13
14 }

Oczywisty i świadomy błąd, który popełniliśmy w powyższym programie polega na zapisaniu elementu N+1-ego w N-wymiarowej tablicy table (linia 12). Kompilujemy program np. poleceniem:

gcc -g -o ex1 ex1.c

i uruchamiamy Valgrinda:

225:user@halo:/home/user# valgrind ./ex1
==10310== Memcheck, a memory error detector.
==10310== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==10310== Using LibVEX rev 1732, a library for dynamic binary translation.
==10310== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==10310== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==10310== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==10310== For more details, rerun with: -v
==10310==
==10310== Invalid write of size 4
==10310==    at 0x4004EA: main (ex1.c:12)
==10310==  Address 0x4D601C4 is 0 bytes after a block of size 404 alloc'd
==10310==    at 0x4A20CFB: malloc (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==10310==    by 0x4004C9: main (ex1.c:9)
==10310==
==10310== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 1)
==10310== malloc/free: in use at exit: 404 bytes in 1 blocks.
==10310== malloc/free: 1 allocs, 0 frees, 404 bytes allocated.
==10310== For counts of detected errors, rerun with: -v
==10310== searching for pointers to 1 not-freed blocks.
==10310== checked 68,168 bytes.
==10310==
==10310== LEAK SUMMARY:
==10310==    definitely lost: 404 bytes in 1 blocks.
==10310==      possibly lost: 0 bytes in 0 blocks.
==10310==    still reachable: 0 bytes in 0 blocks.
==10310==         suppressed: 0 bytes in 0 blocks.
==10310== Rerun with --leak-check=full to see details of leaked memory.

Valgrind nie dość, że zgłasza nam ten błąd to jeszcze wskazuje w której linii został on popełniony. Co więcej zostaliśmy również poinformowani, w której linii zostało zaalokowane miejsce w pamięci którego dotyczy błąd.

Warto tutaj zauważyć, że gdybyśmy skompilowali nasz program bez opcji -g wówczas Valgrind odnalazłby błąd, ale nie potrafiłby zlokalizować jego źródła w kodzie.


Przykład 2 - odczyt uprzednio zwolnionego bloku pamięci


Rozważmy następujący kod:

 1 #define N 101
 2 #include <stdlib.h>
 3 
 4 int main()
 5 {
 6         int index;
 7         int *table;
 8         int a;
 9 
10         table=(int*) malloc(N*sizeof(int));
11 
12         for(index=0;index<N;index++)
13                 table[index]=index;
14 
15         free(table);
16 
17         a=table[1];
18 
19 }

Powyższy program kompilujemy poleceniem:

gcc -g -o ex2 ex2.c

Następnie uruchamiamy pod kontrolą Valgrinda i znow otrzymujemy wyczerpującą informację o zainstniałym błędzie:

202:user@halo:/home/user# valgrind ./ex2
==2450== Memcheck, a memory error detector.
==2450== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==2450== Using LibVEX rev 1732, a library for dynamic binary translation.
==2450== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==2450== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==2450== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==2450== For more details, rerun with: -v 
==2450== 
==2450== Invalid read of size 4
==2450==    at 0x400546: main (ex2.c:17)
==2450==  Address 0x4D60034 is 4 bytes inside a block of size 404 free'd
==2450==    at 0x4A2087E: free (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==2450==    by 0x40053D: main (ex2.c:15)
==2450== 
==2450== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 1)
==2450== malloc/free: in use at exit: 0 bytes in 0 blocks.
==2450== malloc/free: 1 allocs, 1 frees, 404 bytes allocated.
==2450== For counts of detected errors, rerun with: -v
==2450== All heap blocks were freed -- no leaks are possible.

Podobnie jak poprzednio Valgrind spisał się znakomicie. W tym wypadku wskazuje dokładnie miejsce w kodzie, w którym została zwolniona pamięć do której się odwołujemy.


Przykład 3 - odczyt niezainicjalizowanego bloku pamięci

W kolejnym przykładzie spróbujemy użyć niezainicjalizowanej zmiennej. Tym razem kod będzie króciutki:

1 #include <stdio.h>
2 
3 int main()
4 {
5         int x;
6         printf ("x = %d\n", x);
7         return 0;
8 }

Kompilujemy program:

gcc -g -o ex3 ex3.c

i uruchamiamy pod kontrolą Valgrinda:

224:user@halo:/home/user# valgrind ./ex3
==2718== Memcheck, a memory error detector.
==2718== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==2718== Using LibVEX rev 1732, a library for dynamic binary translation.
==2718== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==2718== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==2718== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==2718== For more details, rerun with: -v
==2718== 
==2718== Use of uninitialised value of size 8
==2718==    at 0x4B64760: (within /lib64/libc-2.5.so)
==2718==    by 0x4B677DE: vfprintf (in /lib64/libc-2.5.so)
==2718==    by 0x4B6E6D9: printf (in /lib64/libc-2.5.so)
==2718==    by 0x4004D1: main (ex3.c:6)
==2718== 
==2718== Conditional jump or move depends on uninitialised value(s)
==2718==    at 0x4B6476A: (within /lib64/libc-2.5.so)
==2718==    by 0x4B677DE: vfprintf (in /lib64/libc-2.5.so)
==2718==    by 0x4B6E6D9: printf (in /lib64/libc-2.5.so)
==2718==    by 0x4004D1: main (ex3.c:6)
==2718== 
==2718== Conditional jump or move depends on uninitialised value(s)
==2718==    at 0x4B67CC1: vfprintf (in /lib64/libc-2.5.so)
==2718==    by 0x4B6E6D9: printf (in /lib64/libc-2.5.so)
==2718==    by 0x4004D1: main (ex3.c:6)
==2718== 
==2718== Conditional jump or move depends on uninitialised value(s)
==2718==    at 0x4B66818: vfprintf (in /lib64/libc-2.5.so)
==2718==    by 0x4B6E6D9: printf (in /lib64/libc-2.5.so)
==2718==    by 0x4004D1: main (ex3.c:6)
x = 0
==2718== 
==2718== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 4 from 1)
==2718== malloc/free: in use at exit: 0 bytes in 0 blocks.
==2718== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==2718== For counts of detected errors, rerun with: -v
==2718== All heap blocks were freed -- no leaks are possible.

Powoli zaczynamy się przyzwyczajać do sukcesów Valgrinda, ale to jeszcze nie koniec...


Przykład 4 - niewłaściwe zwalnianie pamięci

Załóżmy na chwilę, że w naszym dużym kodzie przez przypadek zwlaniamy dwukrotnie ten sam obszar pamięci. W skrócie:

 1 #include <stdlib.h>
 2 
 3 int main()
 4 {
 5         int *table;
 6         table=(int*) malloc(100*sizeof(int));
 7         //blok kodu
 8         free(table);
 9         //blok kodu
10         free(table);
11 }

Kompilujemy podobnie jak w poprzednich przykładach. Wykonanie programu powinno zakończyć się błędem. Valgrind znów pomaga:

234:user@halo:/home/user# valgrind ./ex4
==3121== Memcheck, a memory error detector.
==3121== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==3121== Using LibVEX rev 1732, a library for dynamic binary translation.
==3121== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==3121== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==3121== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==3121== For more details, rerun with: -v
==3121== 
==3121== Invalid free() / delete / delete[]
==3121==    at 0x4A2087E: free (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==3121==    by 0x40051F: main (ex4.c:10)
==3121==  Address 0x4D60030 is 0 bytes inside a block of size 400 free'd
==3121==    at 0x4A2087E: free (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==3121==    by 0x400516: main (ex4.c:8)
==3121== 
==3121== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 1)
==3121== malloc/free: in use at exit: 0 bytes in 0 blocks.
==3121== malloc/free: 1 allocs, 2 frees, 400 bytes allocated.
==3121== For counts of detected errors, rerun with: -v
==3121== All heap blocks were freed -- no leaks are possible.


Przykład 5 - błędne przekazywanie parametrów do funkcji systemowych

Oto kolejny przykład:

 1 #include <stdlib.h>
 2 
 3 int main()
 4 {
 5         char *tekst=(char*) malloc(10);
 6 
 7         (void) write(1 /* stdout */, tekst, 10);
 8 
 9         return 0;
10 }

Valgrind ostrzega nas, że jako parametr przekazujemy niezainicjalizowaną zmienną:

244:user@halo:/home/user# valgrind ./ex5
==3332== Memcheck, a memory error detector.
==3332== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==3332== Using LibVEX rev 1732, a library for dynamic binary translation.
==3332== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==3332== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==3332== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==3332== For more details, rerun with: -v
==3332== 
==3332== Syscall param write(buf) points to uninitialised byte(s)
==3332==    at 0x4BD6890: write (in /lib64/libc-2.5.so)
==3332==    by 0x400525: main (ex5.c:7)
==3332==  Address 0x4D60030 is 0 bytes inside a block of size 10 alloc'd
==3332==    at 0x4A20CFB: malloc (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==3332==    by 0x400509: main (ex5.c:5)
==3332== 
==3332== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 1)
==3332== malloc/free: in use at exit: 10 bytes in 1 blocks.
==3332== malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
==3332== For counts of detected errors, rerun with: -v
==3332== searching for pointers to 1 not-freed blocks.
==3332== checked 68,168 bytes.
==3332== 
==3332== LEAK SUMMARY:
==3332==    definitely lost: 10 bytes in 1 blocks.
==3332==      possibly lost: 0 bytes in 0 blocks.
==3332==    still reachable: 0 bytes in 0 blocks.
==3332==         suppressed: 0 bytes in 0 blocks.
==3332== Rerun with --leak-check=full to see details of leaked memory.

Oprócz komunikatu związanego z błędnym użyciem zmiennej tekst, można zauważyć, że Valgrind doszukał się jeszcze innego błędu. Jest to tzw. "memory leak" (wyciek pamięci), ale o tym za chwilę.

Przykład 6 - operacje na "zazębionych" blokach pamięci

Kolejny przykład wymaga odrobiny komentarza:

1 int main()
2 {
3         char big_buf[1000];
4         char * ptr_1=&big_buf[0];
5         char * ptr_2=&big_buf[400];
6         memcpy(ptr_1,ptr_2,500);
7 }

Funkcja memcpy(void *dest, const void *src, size_t n) jest funkcją systemową służącą do kopiowania n bajtów z miejsca src na miejsce dest. Operacja, którą chcemy wykonać to skopiowanie 500 bajtowego bloku ze wskaźnika ptr_2 na wskaźnik ptr_1. Problem polega na tym, że wykonanie takiej operacji zniszczy źródło, czyli blok pamięci wskazywany przez ptr_2, gdyż zaczyna się on 400 bajtów dalej niż ptr_1. Valgrind znów triumfuje:

258:user@halo:/home/user# valgrind ./ex6
==3999== Memcheck, a memory error detector.
==3999== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==3999== Using LibVEX rev 1732, a library for dynamic binary translation.
==3999== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==3999== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==3999== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==3999== For more details, rerun with: -v
==3999== 
==3999== Source and destination overlap in memcpy(0x7FF0001F0, 0x7FF000380, 500)
==3999==    at 0x4A22993: memcpy (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==3999==    by 0x4004F6: main (ex6.c:6)
==3999== 
==3999== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 1)
==3999== malloc/free: in use at exit: 0 bytes in 0 blocks.
==3999== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==3999== For counts of detected errors, rerun with: -v
==3999== All heap blocks were freed -- no leaks are possible.

Przykład 7 - memory leak

I na koniec obiecany już przypadek "wycieku pamięci". W poniższym kodzie 1000 razy wywoływana jest funkcja alokująca pamięc i zapisująca wartości w tablicy liczb całkowitych. Za każdym razem (oprócz ostatniego) tracimy dostęp do utworzonej pamięci, gdyż nadpisujemy wartość wskaźnika table.

 1 #include <stdlib.h>
 2 #define N 1000
 3 
 4 void update(int *table,int iter)
 5 {
 6         int *tmp;
 7         int i;
 8         tmp=(int*) malloc(N*sizeof(int));
 9         for(i=0;i<N;i++){
10                 tmp[i]=i*iter;
11         }
12         table=tmp;
13 }
14 
15 int main()
16 {
17         int *table;
18         int iter;
19         for(iter=0;iter<1000;iter++)
20                 update(table,iter);
21         return 0; 
22 }

Tym razem przy wywołaniu Valgrinda posłużymy się opcjami --leak-check=full --show-reachable=yes:

274:user@halo:/home/user# valgrind --leak-check=full --show-reachable=yes  ex7
==5415== Memcheck, a memory error detector.
==5415== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==5415== Using LibVEX rev 1732, a library for dynamic binary translation.
==5415== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==5415== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==5415== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==5415== For more details, rerun with: -v
==5415== 
==5415== 
==5415== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 1)
==5415== malloc/free: in use at exit: 4,000,000 bytes in 1,000 blocks.
==5415== malloc/free: 1,000 allocs, 0 frees, 4,000,000 bytes allocated.
==5415== For counts of detected errors, rerun with: -v
==5415== searching for pointers to 1,000 not-freed blocks.
==5415== checked 72,168 bytes.
==5415== 
==5415== 4,000 bytes in 1 blocks are still reachable in loss record 1 of 2
==5415==    at 0x4A20CFB: malloc (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==5415==    by 0x4004D0: update (ex7.c:8)
==5415==    by 0x400529: main (ex7.c:20)
==5415== 
==5415== 
==5415== 3,996,000 bytes in 999 blocks are definitely lost in loss record 2 of 2
==5415==    at 0x4A20CFB: malloc (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==5415==    by 0x4004D0: update (ex7.c:8)
==5415==    by 0x400529: main (ex7.c:20)
==5415== 
==5415== LEAK SUMMARY:
==5415==    definitely lost: 3,996,000 bytes in 999 blocks.
==5415==      possibly lost: 0 bytes in 0 blocks.
==5415==    still reachable: 4,000 bytes in 1 blocks.
==5415==         suppressed: 0 bytes in 0 blocks.

999 tablic straciliśmy na dobre. Jedna jest jeszcze do odratowania. Imponująca precyzja.

Gdzie jest dostępny?

Valgrinda odnajdziemy w każdej popularnej dystrybucji Linuksa. W ICM dostępny jest on na komputerze rekin oraz na klastrze halo. Zachęcamy do korzystania!


Ogłoszenie: Dział SUPERUSER!

Szanowni Państwo,


Chcielibyśmy aby od następnego numeru w Biuletynie pojawiały się artykuły o naszych użytkownikach i ich projektach. Możliwe, że w najbliższym czasie odezwie się do Państwa pracownik Biuletynu z prośbą o komentarz dotyczący własnego grantu oraz pracy w ICM. Będziemy również prosić Państwa o zgodę na publikację przekazanych nam informacji. Serdecznie zachęcamy do współpracy! Jeśli ktoś z Państwa ma swój własny pomysł na zareklamowanie swojego projektu w Biuletynie, to bardzo prosimy o kontakt na adres e-mail:

m.cytowski@icm.edu.pl


Serdecznie pozdrawiamy,

Redakcja Biuletynu