(K) Valgrind-harjoitus

Huomautus

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

Macilla saat kuitenkin valgrindin käyttöösi ssh:n avulla: Kirjoita Macin komentorivillä komento:

ssh user_id@linux-desktop.tuni.fi

missä kohdan user_id tilalle kirjoitat oman käyttäjätunnuksesi. Jos tulostuu kysymys “Are you sure you want to continue …”, voit vastata “yes”. Tämän jälkeen jatka, kuten on kerrottu vähän alempana löytyvässä kohdassa Komennon valgrind suorittaminen komentorivillä.

Tavoite: Opin käyttämään valgrind-työkalua muistinhallinnan ongelmien jäljittämiseen ja tulkitsemaan tämän työkalun tulosteita.

Tämä on tärkeää siksi, että lopuissa projekteissa yksi läpäisyvaatimus on, että ohjelmassa ei ole muistin käsittelyyn liittyviä ongelmia. Siitä on tarkoitus varmistua ohjelman testausvaiheessa valgrind-työkalun avulla.

Ohjeita: Hae ohjelmakoodipohja: templates/09/valgrind/ -> student/09/valgrind/.

Tutkitaan seuraavaa pientä ohjelmaa, josta jo pintapuolisesti tutkimalla huomataan useita muistin käsittelyyn liittyviä ongelmatilanteita:

#include <iostream>

using namespace std;

int main() {
    int number1;
    int number2 = 111;
    int *ptr1 = new int;
    int *ptr2 = new int(222);

    cout << number1 << " "
         << number2 << " "
         << *ptr1 << " "
         << *ptr2 << endl;

    delete ptr1;

    *ptr1 = 333;
}

Seuraavissa kohdissa annetaan ohjeet valgrind-työkalun käyttöön sekä Qt Creatorissa että komentorivillä. Mikäli mahdollista kannattaa kokeilla näitä molempia ja vertailla niitä toisiinsa.

Komennon valgrind suorittaminen Qt Creatorissa

Avaa student/09/valgrind/-hakemistoon siirtämäsi projekti Qt Creatorissa.

Jos haluat, voit suorittaa ohjelman. Application Output -ikkunaan pitäisi tulostua “exited with code 0”, eli ohjelman suoritus päättyi paluuarvolla EXIT_SUCCESS, mikä tarkoitaa, että kaikki meni hyvin (siitä huolimatta, että ohjelmassa ei oikeastaan ole mitään muuta kuin virheitä). Huomaa, että kun ohjelmassa on näin paljon muistinkäsittelyyn liittyviä ongelmia kuin tässä esimerkkiohjelmassa, niin ei ole mitenkään taattua, että sen suorittaminen sujuu ongelmitta. Tehtävää laadittaessa ohjelman on pystynyt suorittamaan loppuun asti linux-desktopille asennetulla kääntäjäversiolla, mutta emme voi taata, että ohjelma toimii samoin kaikissa ympäristöissä.

Suorita valgrind valitsemalla Analyze > Valgrind Memory Analyzer. Ohjelma käynnistyy, mutta tällä kertaa Application Output -ikkunaan tulostuu “Analyzing finished”.

Lisäksi Qt Creatorissa aukeaa ikkuna Memcheck, jonka mustassa yläpalkissa pitäisi lukea “Memory Analyzer Tool finished, 8 issues were found”. Tässä tehtävässä tutkimme tämän ikkunan sisältöä. Jos Qt Creatorisi ei näytä otsikoiden Issue ja Location alla mitään, klikkaa ikkunan mustassa yläpalkissa olevaa filtteriä esittävää kuvaketta ja rastita kohta “External Errors”, niin valgrind-työkalun löytämät 8 ongelmaa ilmestyvät listaan.

Qt Creatorin Memcheck-ikkunassa on kaksi saraketta: Issue ja Location. Location-sarakkeeseen pääsee käsiksi kahdella tavalla sen jälkeen, kun on alas päin osoittavaa nuolen kärkeä klikaten avannut jonkin pitkistä riveistä tutkiaksesi sen sisältöä:

  • vierittämällä Memcheck-ikkunan alareunan vierityspalkkia oikealle
  • asettamalla hiiren kursorin sen rivin kohdalle, johon liittyvästä Location-sarakkeesta olet kiinnostunut ja odottamalla, kunnes näkyviin ilmestyy laatikko, jossa on kohta Location niiden rivien tapauksessa, jolloin Location-sarake sisältää jotakin.

Komennon valgrind suorittaminen komentorivillä

Käynnistä komentorivikäyttöliittymä ja navigoi hakemistoon, jossa em. ohjelmakoodi on talletettuna (edellisen kohdan projektihakemisto).

  1. Käännä lähdekoodisi käsin syöttämällä Linux-komentoriville käännöskäsky:

    g++ -std=c++11 -Wall -g main.cpp
    

    Jos lähdekoodissa ei ollut virheitä, syntyy tuloksena suoritettava ohjelma (konekielitiedosto) nimeltä a.out.

  2. Jos haluat, voit kokeilla ohjelman suorittamista kirjoittamalla Linux-komentoriville käskyn:

    ./a.out
    
  3. Suorita seuraavaksi valgrind-komento valmiiksi käännetylle ohjelmalle:

    valgrind --quiet --leak-check=full ./a.out
    

    Jos ohjelmassa ei ole muistinkäsittelyyn liittyviä ongelmia, näytölle ei tulostu mitään ylimääräistä oman ohjelmasi tulosteiden lisäksi.

    Koska tässä esimerkkiohjelmassa oli muistinkäsittelyongelmia, valgrind tulostaa käsittämättömän määrän informaatiota, jota alamme nyt tutkia. Sinun pitää katsoa tulostetta alusta alkaen, eli vieritä terminaalia ylöspäin niin pitkälle, että näet kirjoittamasi valgrind-komennon. (Vinkki: terminaali-ikkuna kannattaa myös venyttää mahdollisimman korkeaksi työskentelyn helpottamiseksi.)

Komentorivillä tulosteen jokaisen rivin alussa näkyy jotain tämäntyylistä “==6647==” ja joillakin riveillä lisäksi jotakin tämäntyylistä “at 0x4F31BE3:”. Qt Creatorissa näitä ei näytetä ollenkaan, joten voit päätellä, että ne eivät ole olennaisia tietoja ja voit lukea komentorivitulostetta siten, että et huomioi näitä ollenkaan.

Alustamattoman muuttujan käyttäminen

Työkalun valgrind virheilmoitukset eivät ole selkeimpiä mahdollisia, joten jatkossa ymmärrät niitä paremmin, kun olet tällaisessa yksinkertaisessa ohjelmapätkässä nähnyt, mitä mistäkin virheestä tulostuu.

Tutki siis ohjelmakoodia ja sen aiheuttamia virheilmoituksia, ja selvitä, minkälaisen virheilmoituksen valgrind antaa alustamattoman muuttujan käyttämisestä?

Muistin vapauttamatta jättäminen

Minkälaisen virheilmoituksen valgrind antaa muistin vapauttamatta jättämisestä?

Jo vapautetun muistin käyttäminen

Minkälaisen virheilmoituksen valgrind antaa jo vapautetun muistin käyttämisestä?

Komennon valgrind suorittaminen

Staattinen analyysi tarkoittaa sitä, että analysoidaan ohjelmakoodia ilman sen suorittamista. Dynaaminen analyysi taas tarkoittaa sitä, että analysoidaan ohjelman toimintaa tietyllä suorituskerralla (esimerkiksi tietyillä syötteillä). Dynaamisessa analyysissä ohjelmasta löytyvät vain ne virheet, jotka kyseisessä suorituksessa tulevat ilmi.

Mitä voit päätellä siitä, että Qt Creator käynnistää ohjelman suorituksen prosessi-ikkunassa aina, kun suoritat valgrind-analyysin ohjelmalle?

Komentorivivivut

Suorita valgrind vielä uudelleen komentoriviltä. Tällä kertaa kirjoita komennoksi:

valgrind --verbose --leak-check=full ./a.out

eli vivun --quiet sijaan käytätkin vipua --verbose. Miten tämä vaikuttaa tulosteeseen?

Komentorivin käyttämisestä taas

Materiaaliosiossa 13.1 haastateltu ohjelmistoammattilainen kertoo: “Vakavassa ohjelmistokehityksessä tarvittavien build-työkalujen jotkin ominaisuudet ovat järkevästi saatavilla vain komentorivillä. Graafiset integroinnit ainakin helposti laahaavat perässä, jos niitä tulee ollenkaan.”

Mitä tämä käytännössä tarkoittaa? Esimerkiksi muistinhallinnan virheiden analysoinnin tapauksessa sitä, että kun valgrind aikanaan julkaistiin, sitä käytettiin vain komentorivillä. Qt Creatorin valgrind-liityntä on toteutettu vasta jossakin vaiheessa myöhemmin.

Firmat haluavat usein käyttää uusimpia työkaluja, joten ammattilaisen on tärkeää osata käyttää myös komentoriviä.

Lopuksi, kokeile vielä suorittaa Qt Creatorissa Analyze > Valgrind Memory Analyzer with GDB. Huomaat, että tämä käynnistää valgrind-työkalun debuggaustilassa, jolloin ohjelmakoodia voi askeltaa rivi kerrallaan, kuten debuggerissa yleensäkin. Nyt Application Output -ikkunasta löytyy samannäköinen tuloste kuin terminaali-ikkunastakin.

Kun lopetat, varmista, että debuggeri on suljettu!

Useimmiten Qt Creator siirtyy Edit-tilasta Debug-tilaan, kun suoritat komennon valgrind. Joskus voi käydä niin, että Qt Creator ei toimi näin, vaan Memcheck-ikkunan näkymiseksi sinun pitää itse siirtyä Edit-tilasta Debug-tilaan. Tämän “ominaisuuden” (?) ilmentyessä on hyvä ymmärtää, että valgrind suoritetaan Qt Creatorissa aina Debug-tilassa.