Muistinhallinnan analysaattori valgrind¶
Muutaman rivin ohjelmasta muistinkäsittelyvirheet löytyvät silmäilemällä koodia ja miettimällä, mitä tuli tehtyä. Kun ohjelman koko kasvaa, ongelmien löytäminen muuttuu aina vaikeammaksi.
Muistinkäsittelyyn liittyviä ongelmia voi jäljittää ohjelmalla
nimeltä valgrind
, joka pitää suorittaa erikseen.
Se analysoi ohjelmakoodin ja tulostaa virheilmoituksen, jos esimerkiksi
- ohjelmasi varaa dynaamista muistia komennolla
new
, mutta ei vapauta sitä komennolladelete
- ohjelmassa käytetään muuttujaa (dynaamisesti tai automaattisesti varattua), jolle ei ole asetettu arvoa
- yrität käsitellä dynaamisesti varattua muistia, joka on jo vapautettu.
Työkalu valgrind
on asennettuna linux-desktopilla.
Jos haluat, voit myös asentaa sen omalle koneellesi.
Voit suorittaa valgrind
-komennon joko Qt Creatorissa tai komentorivillä.
Seuraavassa tehtävässä valgrind
suoritetaan harjoituksen vuoksi molemmilla
tavoilla, jotta opimme käyttämään sitä monipuolisemmin.
Valgrindin virheilmoitukset¶
Joissakin virheilmoituksissa esiintyy numeroita 1, 4, 8.
Luvut tarkoittavat tyypin tarvitseman muistialueen kokoa tavuina.
Merkkityypille (char
) riittää yksi tavu, kokonaisluku (int
)
tarvitsee neljä tavua ja osoittimet kahdeksan.
Valgrind kertoo sarakkeessa Location
tiedoston ja rivinumeron.
Kyseinen kohta on se, missä virhe ilmenee tai missä se havaitaan.
Korjattava koodikohta voi olla jossakin muualla
(jossakin kohdassa, joka suoritettiin ennen virheen havaitsemiskohtaa).
Seuraavaksi käydään läpi joitakin yleisimpiä virheilmoituksia.
Oletetaan, että meillä on tehtävälistaesimerkin tyylinen ohjelma
(examples/09/task_list/
), jossa on tietue List_item
.
Use of unitialized value of size 8¶
Jos linkitetyn listan alkiolle ei varata muistia (komennolla new
),
vaan kirjoitetaan vain esim. seuraavasti:
List_item* new_item;
jo Qt:n editori ilmoittaa alustamattomasta muuttujasta. Jos tällöin kuitenkin ajaa valgrindin, saadaan otsikon mukainen ilmoitus (ja mahdollisesti myös seuraavan otsikon mukainen ilmoitus).
Jos osoittimelle sijoittaa vain arvon nullptr
:
List_item* new_item = nullptr;
eikä tietueen kenttiin yritä sijoittaa mitään, valgrind ei anna mitään ilmoitusta, mutta ohjelma ei toimi oikein.
Jos yllä olevan lisäksi yrittää käyttää alkiota new_item
eli
List_item* new_item = nullptr;
new_item->task = ...;
ohjelma ei tee mitään, eikä valgrind anna mitään ilmoitusta.
Conditional jump or move depends on uninitialised value(s)¶
Tietueen kenttä tai jokin muu muuttuja on jäänyt alustamatta.
Esimerkiksi linkitetyssä listassa jollekin tietueen kentistä ei ole
annettu arvoa.
Jos alustamaton kenttä on jokin muu kuin next
-kenttä, virhe voi
ilmetä, kun kyseisen kentän arvoa yritetään tulostaa.
Jos next
-kenttään ei sijoiteta mitään arvoa (ei edes nullptr
),
ohjelma voi näyttää toimivan normaalisti.
Toisaalta tästä voi aiheutua myös ikuinen silmukka, kun listaa
käydään läpi niin kauan, kun next
-kentän arvo ei ole nullptr
.
Invalid read of size N¶
Yritetään käyttää (lukea) jo vapautettua muistialuetta. Esimerkiksi:
delete item_to_be_removed;
...
cout << item_to_be_removed->task << endl;
Tässä task
-kenttä on merkkijono, jolloin virheilmoituksessa N
on 8.
Jos tulostettava kenttä olisi int
-tyyppinen, N
olisi 4.
Invalid free() / delete / delete[]¶
Yritetään vapauttaa jo vapautettua muistia. Esimerkiksi:
delete item_to_be_removed;
...
delete item_to_be_removed;
Ohjelma voi silti toimia oikein, eli virhettä on vaikea havaita ilman valgrindia.
Jos välillä sijoitetaan arvo nullptr
esimerkiksi:
delete item_to_be_removed;
item_to_be_removed = nullptr;
...
delete item_to_be_removed;
valgrind ei anna mitään ilmoitusta, ja ohjelma voi näyttää toimivan oikein.
N (…) bytes in M blocks are definitely lost in loss record X of Y¶
Muistia jää vapauttamatta, eli delete
-komento unohtui.
Ohjelma voi silti toimia oikein, eli virhettä on vaikea havaita ilman valgrindia.