Debuggerin käyttö
Johdanto: ohjelman suorittaminen vaiheittain
Usein on tilanteita, joissa on hyödyksi suorittaa ohjelmaa vähä vähältä, komento kerrallaan, ja tutkia ohjelman tilaa (muuttujien arvoja) suorituksen eri vaiheissa. Esimerkiksi:
- Olet kirjoittanut ohjelman, joka on syntaktisesti kelvollista Pythonia, mutta ei toimi oikein. Se esimerkiksi tuottaa
vääriä tuloksia tai "kaatuu" ohjelman ajon aikana. Virhe täytyisi etsiä ja poistaa. Toisin sanoen: ohjelmassa on toimintavirhe eli
bugi ja ohjelma täytyisi debugata. Et kuitenkaan ole varma, mikä ohjelman suorituksessa oikein menee pieleen, ja
olisi kiva, jos ohjelman ajon etenemistä voisi seurata vaihe vaiheelta.
- Olet töissä ohjelmointifirmassa ja saanut käsiisi sinulle tuntemattoman ohjelman lähdekoodin. Sinun pitäisi opetella,
miten ohjelma toimii ja tehdä siihen laajennuksia. Ohjelma on melko monimutkainen kokonaisuus ja sen vaiheittainen suorittaminen
helpottaisi sen ymmärtämistä.
- Olet ohjelmoinnin opiskelija ja oppinut juuri jonkin uuden ohjelmointiin liittyvän käsitteen (esim. parametrien välittäminen
metodilta toiselle) tai uusia käskyjä (esim.
while
-silmukkakäskyn). Otat valmiin esimerkkiohjelman (tai laadit itse pienen kokeiluohjelman) ja sitä vähä vähältä suorittaen selvität, miten nämä uudet oppimasi käskyt oikein toimivatkaan.
Vaiheittaisesta suorittamisesta siis olisi hyötyä. Miten sen voi tehdä? Triviaali ratkaisu on sijoittaa tulostuslauseita
(esim. print
) pitkin ohjelmaa siten, että ne tulostavat aina tietyissä kohdissa muuttujien arvoja
näytölle. Kun sitten ajat tälläisia "debugtulosteita" sisältävää ohjelmaa, tulostuu liuta peräkkäisiä tilatietoja,
joiden perusteella voit yrittää päätellä, miten ohjelman suoritus on edennyt (ja missä se menee vikaan, jos kyse on
virheenetsinnästä).
Tulostuslauseiden käyttö on kuitenkin melko työlästä. Niitä joutuu usein kirjoittamaan paljon eri puolelle ohjelmaa. Tulosteiden muotoilu vie aikaa ja hyvienkin debugtulosteiden tulkitseminen voi olla hankalaa. Lisäksi tulostamiskäskyt pitää aina muistaa poistaa ohjelmasta ennen kuin ohjelma annetaan loppukäyttäjälle (ellei perustulostuskäskyjen sijaan käytetä jotain hienostuneempaa tulostusmenettelyä.
Hyvin usein olisi kätevämpää, jos ohjelmakoodia ei tarvitsisi erikseen muokata ohjelman tilan tarkkailemiseksi...
Debuggeri
Debuggeri on apuohjelma, jonka avulla voi suorittaa ja tutkia muita ohjelmia vaihe vaiheelta. Se tarjoaa toimintoja tutkittavan ohjelman yksittäisten rivien tai käskyjen suorittamiseen, ja osaa näyttää tutkittavan ohjelman muuttujien arvot suorituksen eri vaiheissa. Debuggeri voi olla erillinen ohjelma tai se voi olla integroitu sovelluskehittimeen. Esimerkiksi Eclipsen Pydev-lisäosaan sisältyy python-debuggeri.
Tutkittavan ohjelman vaiheittainen suoritus debuggerissa voi alkaa joko ohjelman alusta tai jostain myöhemmästä käyttäjän määräämästä kohdasta. Kohtaa, josta vaiheittainen suorittaminen ja ohjelman tarkastelu aloitetaan, kutsutaan keskeytyskohdaksi eli breakpointiksi. Debuggerin käyttäjä asettaa breakpointin ennen debuggauksen aloittamista.
Debuggerivideot
Oheiset videot auttavat konkreettisesti debuggerin opettelussa.
Video: Virheiden etsintä (Eclipsen debuggerilla), osa 1
Video: Virheiden etsintä (Eclipsen debuggerilla), osa 2
Yksinkertaisen ohjelman tutkiminen Eclipsen debuggerilla
Olkoon tutkittavana pieni ohjelma, jossa on ainoastaan pääohjelma. Ohjelman tutkiminen Eclipsen debuggerissa onnistuu seuraavasti.
- Aseta breakpoint sille ohjelman riville, jolta haluat aloittaa ohjelman tutkimisen rivi riviltä.
Esimerkiksi ensimmäiselle ohjelman sisältämälle riville, jos haluat tutkia koko ohjelmaa alusta alkaen.
Breakpointin asettaminen onnistuu Eclipsessä klikkaamalla hiiren oikealla napilla sen halutun rivin vasemmalla puolella näkyvää
harmaata palkkia tai rivinumeroa, ja valitsemalla Toggle Breakpoint. Riville tulee näkyviin vihreä pieni merkki. (Breakpointin saa myöhemmin haluttaessa poistettua
samasta valikosta.)
- Käynnistä debuggeri klikkaamalla pientä ötökän näköistä ikonia toimintopalkista tai valitsemalla Debug As -> Python Run joko Eclipsen Run-valikosta tai oikealla
hiirennapilla Package Explorerissa esiin tulevasta kontekstivalikosta. Eclipse siirtyy tällöin normaaliin Python-koodin
editointiin tarkoitetusta ns. Pydev-perspektiivistä toiseen, debuggaukseen tarkoitettuun Debug-perspektiiviin. Esiin tulee hieman
erilaisia välilehtiä kuin ennen.
- Debug-perspektiivissä se koodirivi, jota ollaan seuraavaksi suorittamassa, näkyy vihreällä korostettuna. Valitsemalla Run
-> Step Over tai painamalla F6-näppäintä tulee tämä rivi suoritetuksi ja suoritus etenee seuraavalle riville. Tätä
toimenpidettä toistamalla voidaan vähitellen suorittaa koko ohjelma.
- Muuttujien arvot näkyvät Debug-perspektiivissä Variables-välilehdellä Eclipse-ikkunan oikeassa yläkulmassa. Ne muuttuvat sitä mukaa kun ohjelman sisältävät käskyt sijoittavat muuttujiin uusia arvoja. Muuttuneen muuttujan arvo korostuu keltaisella taustavärillä.
Debuggausajon saa katkaistua punaisella "stop-napilla" Debug-perspektiivin yläosassa tai Run-valikosta. Takaisin tavalliseen Pydev-editointiperspektiiviin pääsee valitsemalla "Pydev" Eclipse-ikkunan oikeasta ylänurkasta. Tai jos ko. painiketta ei näy kuten yllä olevissa kuvissa, niin paina valittuna olevan Debug-painikkeen vieressä olevaa >>-painiketta, jolloin Pydev-vaihtoehto tulee näkyviin.
Funktio- ja Metodikutsujen tutkiminen Eclipsen debuggerilla
Yleensä ohjelmassa on muutakin kuin vain pääohjelma ja eri funktiot ja metodit kutsuvat toisiaan. Kuitenkin jos suoritat debuggerissa koodiriviä, jolta kutsutaan toista funktiota/metodia, ja painat F6 (Step Over), niin tuo kutsu kyllä suoritetaan, mutta sen sisäistä toimintaa ei mitenkään näytetä debuggerissa. Jos haluat tutkia, miten kutsuttu funktio/metodi toimii sisäisesti ja suorittaa tuon funktion/metodin ohjelmakoodia rivi riviltä, paina sen sijaan F5 (Step Into). Tällöin ohjelman suoritus siirtyy kutsutun funktion/metodin sisään ja palaa kutsuvalle riville vasta sitten, kun kutsuttu funktio/metodi on hoitanut hommansa pala palalta.
Graafisen sovelluksen tutkiminen Eclipsen debuggerilla
Monissa sovellusohjelmissa, joissa on graafinen käyttöliittymä, on hyvin yksinkertainen käynnistysmetodi, joka vain laittaa käyttöliittymäikkunan näkyviin ruudulle. Ohjelman ns. pääsäikeen suoritus päättyy tämän jälkeen eikä käynnistysmetodi tee muuta. Käyttöliittymäikkuna jää silti ruudulle ja ottaa vastaan käyttöliittymätapahtumia ja reagoi niihin.
Tällaisen käynnistysmetodin tutkiminen debuggerilla on yleensä turhaa. Useammin olemme kiinnostuneempia siitä, miten sovellusohjelma käyttäytyy silloin, kun käyttöliittymässä tapahtuu jotain (esim. käyttäjä painaa jotain nappulaa), siis siitä, miten tapahtumiin reagoiva tapahtumankäsittelijämetodi sisäisesti toimii. Debuggerilla pääsee tähän toimintaan käsiksi sijoittamalla breakpointin tapahtumankäsittelijämetodin (esim. metodi, joka käsittelee hiiren klikkauksen) ensimmäiselle riville. Nyt kun käyttäjä aiheuttaa kyseisenlaisen tapahtuman (painaa nappia), niin breakpoint laukeaa, ohjelman suoritus pysähtyy ja siirrytään Debug-perspektiiviin tutkimaan tapahtumankäsittelijän ohjelmakoodia vähä vähältä.