Dokumentti luotu: 31.3.2004, Pekka Paalanen
Kommentteja ja korjauksia saa lähettää osoitteeseen
paalanen@lut.fi
Sivu 1:
Esittely
Valgrind LTY:ssä
Käyttö
Virhehavaintojen tulkintoja
Sivu 2 (lähinnä lisätietoa kiinnostuneille):
Tekniset vaatimukset
Pitkä esimerkkiajo
Vastaantulleita kummallisia tilanteita
Valgrind on näppärä muistidebuggeri x86/Linux ympäristöön. Sen käyttö on helppoa, eikä se vaadi testattavalta ohjelma tavallisesti mitään erikoista. Valgrind havaitsee monia sellaisia virheitä ohjelmista, jotka muuten olisivat jääneet kokonaan huomaamatta tai niiden löytäminen olisi hyvinkin työlästä.
Valgind havaitsee seuraavanlaisia virheitä (lainaus):
Valgrind on komentoriviohjelma, joka ajaa (tavallaan tulkkaa) ajettavan ohjelman ja raportoi virheistä. Valgrind osaa myös muita temppuja kuin mitä tässä dokumentissa on esitetty, mutta tässä käsitellään vain Valgrindin muistinkäsittelyn tarkkailua (memcheck) C-kielellä kirjoitetuissa ohjelmissa.
Valgrind on suunniteltu lähinnä C ja C++ ohjelmille, mutta sillä voi tutkia millä tahansa kielellä tehtyjä ohjelmia, kunhan ohjelma on muutenkin ajettavissa. Lisäksi Valgrind ei tutki ainoastaan juuri sitä ajettavaa ohjelmaa, vaan myös kaikkia valmiita kirjastofunktioita, joita ohjelma käyttää. Ohjelmaa ei (välttämättä) tarvitse erikseen kääntää Valgrindiä varten tai linkittää johonkin erityiseen kirjastoon.
Valgrindin huono puoli on mm. se, että sen alla ohjelmat suoritetaan huomattavasti hitaammin. Oletuksena käytössä olevan muistinkäsittelyn tarkkailijan kanssa 10-30 kertaa hitaammin kuin normaalisti.
Valgrind löytyy Pentinkulman koneista, luokka 6303. Tavallisia C-kielellä koodattuja harkkatöitä voi ajaa suoraan Valgrindillä, ja se myös kannattaa.
Valgrindia käytetään käännettyyn ohjelmaan (binääriin), ei lähdekoodiin. C-kielinen ohjelma kannattaa kääntää -g optiolla, esimerkiksi:
gcc -W -Wall -g -o ohjelma koodi.cValgrind osaa lukea mukaan kääntyvää debug-informaatiota ja kertoo selkeämmin, missä kohtaa koodia virhe tulee.
Kovemman tason optimoinnit saattavat joskus sekottaa muistidebuggeria, joten optimointi kannattaa jättää pois tai käyttää korkeintaan -O1 tasoa.
Itse Valgrindin käyttäminen on yksinkertaista:
valgrind ./ohjelmaValgrind ajaa ohjelman ja tulostelee samalla raporttiaan. Oletuksena Valgrindin ilmoitukset menevät standardiin virhevirtaan eli stderr:iin, siis normaalisti ruudulle.
Valgrindille on myös optioita olemassa, joista saa listan komennolla valgrind --help, mutta man- tai info-sivua sillä ei ole. Dokumentaatiota löytyy Valgrindin kotisivulta englanniksi. Yleisimmät optiot ovat:
Seuraavassa on esitetty pätkiä Valgrindin tulosteista kertoen, mitä ne tarkoittavat.
==3863== Invalid write of size 1 ==3863== at 0x40021F47: strcpy (in /usr/lib/valgrind/vgskin_memcheck.so) ==3863== by 0x8048807: kasittely (korjattu.c:129) ==3863== by 0x804856D: main (korjattu.c:23) ==3863== by 0x40263DCB: __libc_start_main (in /lib/libc-2.3.2.so) ==3863== Address 0x410BC6B0 is 0 bytes after a block of size 4 alloc'd ==3863== at 0x4002AA4D: malloc (in /usr/lib/valgrind/vgskin_memcheck.so) ==3863== by 0x80487C7: kasittely (korjattu.c:123) ==3863== by 0x804856D: main (korjattu.c:23) ==3863== by 0x40263DCB: __libc_start_main (in /lib/libc-2.3.2.so)Ohjelma on yrittänyt kirjoittaa yhden tavun väärään paikkaan muistia. Monesti tällainen johtaisi segmentation fault:iin, mutta se ei ole mitenkään taattua. Normaalisti virhe olisi jäänyt havaitsematta ja tuottanut hyvin kummallisia tuloksia.
==3863== Invalid read of size 1 ==3863== at 0x40021F18: strlen (in /usr/lib/valgrind/vgskin_memcheck.so) ==3863== by 0x4029655E: _IO_vfprintf (in /lib/libc-2.3.2.so) ==3863== by 0x4029D131: _IO_printf (in /lib/libc-2.3.2.so) ==3863== by 0x8048831: kasittely (korjattu.c:133) ==3863== Address 0x410BC6B0 is 0 bytes after a block of size 4 alloc'd ==3863== at 0x4002AA4D: malloc (in /usr/lib/valgrind/vgskin_memcheck.so) ==3863== by 0x80487C7: kasittely (korjattu.c:123) ==3863== by 0x804856D: main (korjattu.c:23) ==3863== by 0x40263DCB: __libc_start_main (in /lib/libc-2.3.2.so)Tässä taas on luettu yksi tavu väärästä paikasta, muuten samantapainen tilanne kuin edellisessä. Funktiosta kasittely on kutsuttu printf funktiota (joka näkyy tässä nimellä _IO_printf). Koska virhe tapahtui funktiossa strlen, voisi arvella, että on yritetty tulostaa merkkijonoa, joka ei ole nul-terminoitu.
==3826== Conditional jump or move depends on uninitialised value(s) ==3826== at 0x804899C: parser (koodi2.c:140) ==3826== by 0x80490F2: main (koodi2.c:371) ==3826== by 0x40263DCB: __libc_start_main (in /lib/libc-2.3.2.so) ==3826== by 0x8048420: (within /home/tester/desk/ht1)Funktiossa parser tiedoston koodi2.c rivillä 140 on jokin ehtolauseke, jonka tulos riippuu alustamattoman muuttujan (muistipaikan) arvosta. Paikallisia muuttujia tai malloc:lla varattuja muistialueita ei automaattisesti alusteta, vaan ne sisältävät mitä sattuu. Ohjelman toiminta ei saa riippua alustamattomista muuttujista, koska tulos voi olla mitä tahansa.
==22678== Use of uninitialised value of size 4 ==22678== at 0x80487C1: laske_luvut (koodi.c:120) ==22678== by 0x8048E65: main (koodi.c:264) ==22678== by 0x40263DCB: __libc_start_main (in /lib/libc-2.3.2.so) ==22678== by 0x80484C0: (within /home/tester/desk/ht1)Vähän sama tilanne kuin edellisessä, ohjelma on käyttänyt alustamatonta muuttujaa esimerkiksi jossakin laskuoperaatiossa. Muuttujan koko on neljä tavua, joten se on luultavasti int tyyppiä.