Muistinhallinnan analysaattori valgrind

Huomautus

Valgrind toimii Linux-etätyöpöydällä, mutta todennäköisesti ei Mac- eikä Windows-koneilla (tai valgrindin asentaminen näille koneille ei onnistu).

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ä komennolla delete
  • 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. Tämä johtuu siitä, että nullptr ei osoita mitään muistipaikkaa, joten mitään ei voida vapauttaakaan.

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.