4.2 Muistinhallintaa avustava kirjasto Tutnew



Samankaltaiset tiedostot
Tutnew työkalu C++:n dynaamisen muistinhallinnan testaamiseen

Ohjelmoinnin perusteet Y Python

4.1 C++-kielen tyylianalysaattori Style++

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

Dynaaminen muisti. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät 2017.

Kääntäjän virheilmoituksia

Osoitin ja viittaus C++:ssa

Harjoitustyö: virtuaalikone

Näin järjestän ohjelmointikurssin, vaikka en ole koskaan ohjelmoinut

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

PERL. TIE Principles of Programming Languages. Ryhmä 4: Joonas Lång & Jasmin Laitamäki

812347A Olio-ohjelmointi, 2015 syksy 2. vsk. X Poikkeusten käsittelystä

C++ Ohjelmoijan käsikirja. Johdanto

Rajapinnat ja olioiden välittäminen

C-ohjelmoinnin peruskurssi. Pasi Sarolahti

Tähtitieteen käytännön menetelmiä Kevät 2009

11/20: Konepelti auki

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

Rakennusautomaation käytettävyys. Rakennusautomaatioseminaari Sami Karjalainen, VTT

AS C-ohjelmoinnin peruskurssi 2013: C-kieli käytännössä ja erot Pythoniin

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

Esimerkkiprojekti. Mallivastauksen löydät Wroxin www-sivuilta. Kenttä Tyyppi Max.pituus Rajoitukset/Kommentit

ELM GROUP 04. Teemu Laakso Henrik Talarmo

4. Lausekielinen ohjelmointi 4.1

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

2 Konekieli, aliohjelmat, keskeytykset

Automaattinen muistinhallinta

Ohjelmointi 1. Kumppanit

Automaattinen yksikkötestaus

Johdatus ohjelmointiin

Pythonin alkeet Syksy 2010 Pythonin perusteet: Ohjelmointi, skriptaus ja Python

TIE Ohjelmistojen testaus 2015 Harjoitustyö Vaiheet 1 ja 2. Antti Jääskeläinen Matti Vuori

Veli-Matti Taskila asiantuntija Suomen ammattikorkeakouluopiskelijakuntien liitto SAMOK ry.

Ei raportteja roskiin

KURSSIPALAUTE KÄYTÄNNÖSSÄ: MITÄ JA MITEN?

Tähtitieteen käytännön menetelmiä Kevät 2009 Luento 4: Ohjelmointi, skriptaus ja Python

Dynaaminen muisti Rakenteiset tietotyypit

TIETOKONE JA TIETOVERKOT TYÖVÄLINEENÄ

Järjestelmän asetukset. Asetustiedostojen muokkaaminen. Pääkäyttäjä eli root. Järjestelmänhallinnan työkalut

TT00AA Ohjelmoinnin jatko (TT10S1ECD)

Ohjelmoinnin perusteet Y Python

UML -mallinnus TILAKAAVIO

Ohjelmoinnin perusteet Y Python

Taulukot. Jukka Harju, Jukka Juslin

TIE Tietorakenteet ja algoritmit 1. TIE Tietorakenteet ja algoritmit

L models. Käyttöohje. Ryhmä Rajoitteiset

Harjoitustyö 3 - Millosemeni

Ohjelmoinnin peruskurssien laaja oppimäärä

5. HelloWorld-ohjelma 5.1

Ohjelmistojen mallintaminen Olioiden yhteistyö Harri Laine 1

Ohjelmointi 1 / syksy /20: IDE

Alkukartoitus Opiskeluvalmiudet

9. Luento: Ohjelmistotyö. Tommi Mikkonen,

Uutisjärjestelmä. Vaatimusmäärittely. Web-palvelujen kehittäminen. Versio 1.3

C++11 lambdat: [](){} Matti Rintala

815338A Ohjelmointikielten periaatteet

Automaattinen regressiotestaus ilman testitapauksia. Pekka Aho, VTT Matias Suarez, F-Secure

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

TIEA241 Automaatit ja kieliopit, syksy Antti-Juhani Kaijanaho. 30. marraskuuta 2015

Ohjelmoinnin perusteet Y Python

TIE PRINCIPLES OF PROGRAMMING LANGUAGES Eiffel-ohjelmointikieli

GSRELE ohjeet. Yleistä

PRINCIPLES OF PROGRAMMING LANGUAGES - DEBUGGER

<e.g. must, essential, conditional>

Muuttujien roolit Kiintoarvo cin >> r;

Näkökulmia tietoyhteiskuntavalmiuksiin

C-ohjelmointikielen perusteet, osa 1

Dart. Ryhmä 38. Ville Tahvanainen. Juha Häkli

15. Ohjelmoinnin tekniikkaa 15.1

Kiinan kursseilla 1 2 painotetaan suullista kielitaitoa ja kurssista 3 alkaen lisätään vähitellen myös merkkien lukemista ja kirjoittamista.

Maastotietokannan torrent-jakelun shapefile-tiedostojen purkaminen zip-arkistoista Windows-komentojonoilla

Electronic Frontier Finland ry

Tik Tietojenkäsittelyopin ohjelmatyö Tietotekniikan osasto Teknillinen korkeakoulu. LiKe Liiketoiminnan kehityksen tukiprojekti

System.out.printf("%d / %d = %.2f%n", ekaluku, tokaluku, osamaara);


Sami Hirvonen. Ulkoasut Media Works sivustolle

Sisältö. 22. Taulukot. Yleistä. Yleistä

Pythonin Kertaus. Cse-a1130. Tietotekniikka Sovelluksissa. Versio 0.01b

etunimi, sukunimi ja opiskelijanumero ja näillä

Ohjelmoinnin perusteet, syksy 2006

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

TIE Ohjelmistojen suunnittelu

Seuraavat Windowsin käyttöjärjestelmäversiot tukevat Novell Filr -työpöytäsovellusta:

PROBYTE GSM ALARM #6d

C-kielessä taulukko on joukko peräkkäisiä muistipaikkoja, jotka kaikki pystyvät tallettamaan samaa tyyppiä olevaa tietoa.

SÄHKÖTEKNIIKAN KOULUTUSOHJELMAN KANDIDAATINTYÖOHJE

PÄÄOMINAISUUDET KÄYTTÖOHJE

SEPA diary. Dokumentti: SEPA_diary_PK_HS.doc Päiväys: Projekti: AgileElephant Versio: V0.3

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2

Kirjoita oma versio funktioista strcpy ja strcat, jotka saavat parametrinaan kaksi merkkiosoitinta.

SEPA-päiväkirja: Käytettävyystestaus & Heuristinen testaus

Harjoitus 3 (viikko 39)

Heuristisen arvioinnin muistilista - lyhyt versio

T Olio-ohjelmointi Osa 5: Periytyminen ja polymorfismi Jukka Jauhiainen OAMK Tekniikan yksikkö 2010

TURNITIN-OHJELMAN KÄYTTÖ - OPISKELIJAN OHJE

ENE-C2001 Käytännön energiatekniikkaa. Aloitustapaaminen Osa III: Tekninen raportointi

Objective-C. Ryhmä 35: Ilpo Kärki Aleksi Pälä

Menetelmäraportti - Konfiguraationhallinta

Kansainvälisyys osana korkeakouluopintoja kokemuksia ja haasteita suomalaisista korkeakouluista

Ohjelmoinnin perusteet Y Python

Transkriptio:

48 Harjoitustöiden laadun kehittäminen [5] Edison Design Group. 2000. Compiler Front Ends for the OEM Market, http://www.edg.com/cpp.html/, 21.8.2001. [6] Henricson, M., & Nyquist E. 1992. Programming in C++, Rules and Recommendations, Ellemtel Telecommunication Systems Laboratories. [7] International standard ISO/IEC 14882. 1998. Programming languages C++. American National Standards Institute, New York. 776. [8] Meyers, S. 1992. Effective C++: 50 specific ways to improve your programs, Addison Wesley. 206 s. [9] Uimonen, T. 2000. Tyylin automaattinen arviointi ohjelmistoissa. Diplomityö, Ohjelmistotekniikan laitos, Tampereen teknillinen korkeakoulu. 65 s. 4.2 Muistinhallintaa avustava kirjasto Tutnew Matti Rintala 4.2.1 Tausta C++ on teollisuudessa erittäin suosittu ohjelmointikieli ja siitä johtuen sitä käytetään myös opetuskielenä suuressa osassa oppilaitoksia. C++ on erittäin tehokas ja varsin monikäyttöinen ohjelmointikieli, mutta sitä ei ole suunniteltu opetuskäyttöön, mistä johtuen kieli sisältää monia ansoja ja vaaroja, jotka aiheuttavat ylimääräistä päänvaivaa erityisesti aloittelevalle ohjelmoijalle, jolla usein ei ole käsitystä siitä, mistä ja miten ohjelmassa olevaa virhettä kannattaisi etsiä. Toinen ongelma ohjelmoinnin opetuksessa on, että opetuksessa yleensä korostetaan, että ohjelmien tulisi pystyä reagoimaan järkevästi muistin loppumiseen. Tätä on kuitenkin erittäin vaikea testata, koska nykyisissä tietokoneissa muistia on niin paljon, että sen loppuminen on erittäin epätodennäköistä ja usein vaikea saada aikaan. Tutnew on Tampereen teknillisellä korkeakoululla käytössä oleva kirjasto, joka auttaa ohjelmoijaa C++:n dynaamisen muistinhallinnan virheiden huomaamisessa ja paikantamisessa. Lisäksi Tutnew antaa mahdollisuuden simuloida koneen muistin loppumista sekä saada tilastotietoa dynaamisen muistin käytöstä. Tutnew ei pyri korjaamaan muistinhallinnan virheitä, vaan mahdollisuuksien mukaan antamaan ohjelmoijalle selkeitä virheilmoituksia, jotka kertovat mitä ja missä on mennyt pieleen. Tutnew pyrkii lisäämään mahdollisuutta, että muistinhallinnan

Harjoitustöiden laadun kehittäminen 49 kannalta virheellinen ohjelma toimii virheellisesti ja kaatuu mahdollisimman pian muistinhallintavirheen jälkeen. Koska Tutnew on tarkoitettu myös ohjelmoinnin alkeisopetukseen, on sen käyttö mahdollisimman läpinäkyvää ja helppoa. Käyttääkseen Tutnew tä ohjelmoijan täytyy vain lisätä yksi #include -komento jokaiseen kooditiedostoonsa sekä käskeä kääntäjää linkittämään Tutnew-kirjasto mukaan suoritettavaan ohjelmaan. Tutnew-kirjastoa on käytetty ja testattu TTKK:lla muutaman vuoden ajan ohjelmoinnin perusopetuksessa, jossa siitä on saatu hyviä tuloksia. Opiskelija voi käyttää Tutnew tä joko korkeakoulun tietokoneympäristössä tai asentaa sen kotikoneelleen. Tutnew on toteutettu C++:n kirjastona, joka on kirjoitettu kielen nykyisen standardin ISO 14882 mukaisesti. Se käyttää dynaamisen muistinhallinan testaamiseen vain kielen omia mekanismeja. Niinpä Tutnew pitäisi olla mahdollista kääntää mihin tahansa riittävän uuteen kääntäjäympäristöön käyttöjärjestelmästä ja kääntäjän valmistajasta riippumatta. Tutnew n kehitys on tehty GCC-kääntäjällä ja toimii ainakin kääntäjän versioilla 2.95.2, 2.95.3 ja 3.0. Käännös- ja testausympäristöinä on ollut Redhat Linux ja Sun Microsystemsin Solariksen eri versiot. Näissä ympäristöissä Tutnew on toiminut erinomaisesti. Tutnew n aikaisempia versioita on käytetty myös Windows-ympäristössä djgpp- ja Borland-kääntäjissä. Uudempia versioita ei enää ole testattu noissa ympäristöissä, mutta tiedossa ei ole mitään syytä, miksei Tutnew toimisi näissäkin ympäristöissä. Artikkelin kirjoittaja ottaa mielellään vastaan kokemuksia Tutnew stä muissa ympäristöissä. Tutnew, sen dokumentaatio ja muuta informaatiota on saatavilla sivulta http:// www.cs.tut.fi/~bitti/tutnew/. 4.2.2 Tausta Ohjelmoijan kannalta C++-kielessä muistinhallinta jakautuu kahteen osaan. Kaikkien nimettyjen muuttujien sekä kääntäjän itsensä automaattisesti tuottamien väliaikaisolioiden elinkaari määräytyy ohjelman rakenteesta ja ne tuhotaan automaattisesti. Sen sijaan dynaamisesti new-operaattorilla luotujen olioiden elinkaari ei ole käännösaikaisesti määrätty ja ohjelmoijan täytyy tuhota ne erikseen operaattorilla delete jossain ohjelman vaiheessa. Jos dynaamisesti varattua muistia ei vapauteta missään vaiheessa, tapahtuu muistivuoto. C++-standardi takaa kuitenkin, että kaikki ohjelman dynaamisesti varaama muisti vapautetaan, kun ohjelman suoritus päättyy. Tämän vuoksi dynaamisen muistin vuotoihin ei aina suhtauduta kovin vakavasti. Muistivuodot ovat kuitenkin vakava asia ainakin kahdesta syystä. Jos ohjelman suoritus kestää kauan, muisti-

50 Harjoitustöiden laadun kehittäminen vuodot kuluttavat koneen muistiresursseja, mikä saattaa aiheuttaa joko muistin ennenaikaisen loppumisen tai ohjelman hidastumisen, kun virtuaalimuistijärjestelmä alkaa käyttää kiintolevyä muistin korvikkeena. Toinen muistivuotojen vaikutus on, että vaikka ohjelman dynaamisesti varaama muisti vapautetaankin ohjelman lopussa, ei kyseiseen muistiin luotujen olioiden purkaja-jäsenfunktioita suoriteta. Purkajat puolestaan saattavat sisältää tärkeää toiminnallisuutta kuten muiden resurssien vapautuksia, tiedon päivittämistä kiintolevylle tai tietokantoihin yms. Tämän vuoksi muistivuodot saattavat aiheuttaa muita resurssivuotoja tai ohjelman virheellisen käyttäytymisen. Toinen tyypillinen dynaamisen muistinhallinnan virhe on muistin vapauttaminen useaan kertaan tai muistialueen käyttäminen vielä sen vapauttamisen jälkeen. Näiden virheiden vaikutukset ohjelman toimintaan ovat C++-kielessä määrittelemättömiä. Tyypillisesti ne aiheuttavat joko ohjelman suorituksen keskeytymisen enemmän tai vähemmän selkeään virheilmoitukseen tai ohjelman muistin sotkeentumisen, mikä saattaa aiheuttaa ohjelman virheellisen käyttäytymisen. Monissa ohjelmointikielissä dynaamisen muistinhallinnan virheitä on pyrittä estämään erilaisten muistin roskienkeruujärjestelmien avulla. Nämä vapauttavat ohjelmoijan enemmän tai vähemmän dynaamisen muistinhallinnan suunnittelusta, mikä tietysti helpottaa erityisesti ohjelmoinnin alkeiden opiskelua. C++ ei kuitenkaan sisällä muistin roskienkeruuta. Kielen kehittäjät ovat ilmoittaneet syyksi tähän sen, että C++ on kehitetty erityisesti ohjelmien tehokkuutta silmällä pitäen. Lisäksi automaattinen roskienkeruu sopii huonosti tilanteisiin, jolloin olioiden tuhoutumishetkellä on väliä esimerkiksi sen takia, että olion siivoustoimenpiteet pitää suorittaa tiettyyn aikaan. Tämä on mahdollista, jos siivoustoimenpiteet sisältävät muiden resurssien vapautuksia, io-operaatioita tai käyttöliittymään liittyviä toimenpiteitä. Ohjelman dynaamisessa muistinhallinnassa olevat virheet ovat erityisen hankalia paikantaa. Dynaamisen muistin vapauttamatta jättäminen aiheuttaa muistivuotoja, jotka jäävät yleensä huomaamatta, koska ohjelman muisti vapautetaan kuitenkin ohjelman päättyessä. Niinpä muistivuodot ovat varsin tavallisia jopa tuotantokäytössä olevissa ohjelmissa. Vapautetun muistin käyttäminen vapautuksen jälkeen aiheuttaa myös vaikeita virheitä. Näissä tapauksissa ohjelma saattaa toimia näennäisesti oikein pitkäänkin, kunnes jossain vaiheessa ohjelma ottaa jo vapautetun muistialueen uudelleen käyttöön. Tällöin sama muistialue kuuluu näennäisesti yhtaikaa sekä jo vapautettuun että uuteen olioon, jolloin muistiin yhden olion kautta tehdyt muutokset sotkevat toisen olion muistin. Tällaiset virheet eivät välttämättä edes esiinny joka kerralla, koska muistialueiden uudelleenkäyttämiseen saattavat vaikuttaa koneen fyysisen muistin määrä sekä jopa se, paljonko muita ohjelmia koneessa on käynnissä.

Harjoitustöiden laadun kehittäminen 51 Dynaamisen muistinhallinnan testaamiseen on olemassa useita kaupallisia työkaluja, mutta ne ovat yleensä varsin kalliita ja toimivat vain tietyissä käyttöjärjestelmissä ja kääntäjissä. Lisäksi tällaisten työkalujen käyttö vaatii usein sen verran C++-kielen ja ohjelmien testauksen ymmärtämistä, että ne sopivat huonosti ohjelmoinnin alkeisopetukseen. 4.2.3 Tutnew:n toiminnan kuvaus Kun ohjelma on käännetty Tutnew n kera, Tutnew valvoo automaattisesti ohjelman dynaamista muistinhallintaa ja virheen sattuessa antaa virheilmoituksen. Tutnew n toimintaan voi vaikuttaa ympäristömuuttujien ja esikääntäsymbolien avulla, mutta niitä ei käsitellä tarkemmin tässä artikkelissa. Seuraavassa on lueteltu kaikki ne virheet, jotka Tutnew pystyy joko havaitsemaan ja raportoimaan tai joiden sattuessa Tutnew pyrkii C++-kielen rajoissa sotkemaan ohjelman toiminnan niin paljon, että virhe ilmenisi mahdollisimman aikaisessa vaiheessa. Tutnew:n virheilmoitukset ovat konfiguroitavissa suomen, englannin, tampereen tai pohjanmaan kielille. Muistivuoto tapahtuu, kun ohjelmoija varaa ohjelmassa muistia dynaamisesti new llä mutta ei vapauta sitä. Tällaiset virheet Tutnew pystyy havaitsemaan ja raportoimaan luonnollisesti vasta ohjelman lopussa. Kun Tutnew havaitsee ohjelman lopussa muistivuodon, se raportoi vapauttamatta jätetyn muistialueen koon sekä sen, millä rivillä ja missä tiedostossa kyseinen muistialue on varattu. Ikävä kyllä Tutnew ei kykene ilmoittamaan tarkemmin sitä, minkä tyyppinen olio muistialueella on aikanaan sijainnut. Useimmissa ohjelmissa yhdellä koodirivillä on kuitenkin vain yksi new-komento, joten rivinumero yksilöi vuodon yksikäsitteisesti. Tyypillinen vastareaktio muistivuotojen vaaraan on, että ohjelmoija vapauttaa osoittimien päässä olevaa muistin suunnittelematta ohjelman muistinhallintaa mitenkään erityisesti. Tällöin tyypillinen virhe on, että sama muistialue vapautetaan useassa ohjelman kohdassa eri osoittimia käyttäen. Tutnew pystyy havaitsemaan virheellisen tuhoamisyrityksen ja antamaan siitä virheilmoituksen heti sen sattuessa. Virheilmoitus kertoo, missä muistialue on varattu, missä se on vapautettu ensimmäisen kerran ja missä sitä yritetään vapauttaa uudelleen. Tällaisessa tilanteessa Tutnew normaalisti keskeyttää ohjelman suorituksen, mutta tähän voi ohjelmoija itse vaikuttaa. Useaan kertaan vapauttamisen lisäksi yleinen muistinhallintaongelma on, että dynaamisesti varattu muisti vapautetaan liian aikaisin ja sitä käytetään vielä vapauttamisen jälkeen. (Tämä ongelma esiintyy myös staattisesti varatun muistin yhteydessä, mutta sille Tutnew ei kykene tekemään mitään.) Koska Tutnew on vain kirjasto, sen mahdollisuudet havaita jo vapautettuun muistiin tapahtuvat viit-

52 Harjoitustöiden laadun kehittäminen taukset ovat rajalliset. Kun Tutnew tä käyttävä ohjelma vapauttaa dynaamisesti varaamansa muistin, Tutnew ei todellisuudessa vapauta kyseistä muistialuetta vaan täyttää sen testibittikuviolla ja lisää sen omaan tarkkailukirjanpitoonsa. Tämä auttaa jo vapautetun muistin käytön havaitsemiseen seuraavasti: Jos ohjelma lukee vapautettua muistialuetta, se ei enää löydä sieltä itse kirjoittamaansa dataa vaan Tutnew n testikuvion. (Tämä muistialueen sotkeminen on täysin C++-standardin mukaista, koska vapautettuun muistialueeseen viittaaminen aiheuttaa joka tapauksessa ohjelman määrittelemättömän toiminnon.) Tällöin ohjelma todennäköisesti toimii väärin jos testivaiheessa ja virhe voidaan etsiä ja korjata. Ohjelman lopussa Tutnew käy läpi kaikki vapautetut muistialueet ja tarkastaa, että testikuvio on ehjä. Jos ohjelma on kirjoittanut jo vapautettuun muistialueeseen, muuttaa kirjoitus erittäin todennäköisesti testikuviota. Tällöin Tutnew huomaa muutoksen ja antaa virheilmoituksen. Valitettavasti molemmissa edellisissä tapauksissa Tutnew kykenee vain edesauttamaan virheen olemassaolon havaitsemista. Sen sijaan virheen aiheuttaneen lukemisen tai kirjoittamisen sijainnista ohjelmassa Tutnew ei pysty kertomaan mitään, vaan virhe on paikannettava perinteisin testauskeinoin. Vapautetun muistin tarkastaminen lisää ohjelman todellista muistinkulutusta, koska Tutnew ei todellisuudessa voi vapauttaa muistia. Lisäksi testikuvioiden tuottaminen ja tutkiminen ohjelman lopussa hidastaa ohjelmaa. Tämän vapautetun muistin käytön testin voi myös halutessaan kytkeä pois päältä. C++:ssä taulukoita on tyypillistä käydä läpi osoitinaritmetiikkaa käyttäen. Tällöin saattaa käydä niin, että dynaamisesti varattu taulukko yritetään vapauttaa käyttäen osoitinta, joka ei enää osoitakaan taulukon alkuun. Tällöin ohjelman toiminta on määrittelemätön. Samanlainen virhe saattaa syntyä, jos deletelle annetaan alustamaton osoitin tai osoitin, jonka sisältö on aiemman muistinhallintavirheen vuoksi sotkeentunut. Kolmas tyypillinen tilanne on, että deletellä yritetään osoittimen kautta vapauttaa muuttujaa, joka ei ole new llä varattu. Tutnew tarkastaa aina, että deletelle annettu osoitin on sellainen, joka todella osoittaa jonkin dynaamisesti varatun muistialueen alkuun. Mikäli näin ei ole, antaa Tutnew virheilmoituksen. Taulukoiden yli- tai ali-indeksointi on tyypillinen ohjelmointivirhe C++:ssa (ja muissakin ohjelmointikielissä). Ikävä kyllä C++-standardi ei määrittele, mitä tapahtuu, jos taulukkoa indeksoidaan väärin. Tyypilliset C++ kääntäjät tuottavat koodia, jossa ohjelma yksinkertaisesti lukee muistia taulukon muistialueen jommalta kummalta puolelta. Samanlainen virhe tapahtuu, jos taulukkoa käydään läpi osoitinaritmetiikan avulla ja osoitin pääsee liikkumaan taulukon ulkopuolelle.

Harjoitustöiden laadun kehittäminen 53 Koska nämä virheet eivät suoranaisesti liity dynaamiseen muistinhallintaan, ei Tutnew pysty huomaamaan niitä kovin hyvin. Tutnew pyrkii kuitenkin helpottamaan indeksointivirheiden löytämistä varaamalla pyydetyn muistialueen molemmille puolelle pienen puskurialueen muistia. Tutnew täyttää tämän alueen jo varauksen yhteydessä testikuviolla. Kuten vapautetun muistin käytössäkin, tästä on kaksi hyötyä: Jos ohjelma virheellisesti viittaa taulukon ulkopuolelle indeksointivirheen tai osoitinaritmetiikan takia, ei luettu arvo ainakaan ole nolla vaan testikuvion tuottamaa puppua. Tutnew tarkastaa testikuvion eheyden muistin vapauttamisen ja ohjelman loppumisen yhteydessä. Jos ohjelma on kirjoittanut taulukon ulkopuolelle ja sotkenut testikuvion, antaa Tutnew virheilmoituksen. Yli- ja ali-indeksoinnin havaitsemisessa on sama ikävä puoli kuin vapautetun muistin käytössä Tutnew ei pysty kertomaan, missä päin ohjelmaa virhe on sattunut. Lisäksi on huomattava, että Tutnew n käyttämä puskurialue ei ole kovin suuri (oletusarvoisesti 16 tavua muistialueen molemmilla puolilla), joten suuret yli- ja ali-indeksoinnit jäävät Tutnew ltä huomaamatta. C++ vaatii, että dynaamisesti varatut yksittäiset oliot pitää tuhota komennolla delete ja dynaamisesti varatut taulukot komennolla delete[]. Ikävä kyllä kääntäjä ei pysty varmistamaan, että näin todella tehdään, koska dynaamisesti varattua taulukkoa käsitellään täsmälleen samantyyppisen osoittimen avulla kuin yksittäistäkin oliota. Mikäli edellämainittua C++:n sääntöä rikotaan, on ohjelman toiminta määrittelemätön. Tyypillinen virhe on taulukon vapauttaminen tavallisella deletellä. Useissa kääntäjissä C++:n perustyyppien tapauksessa virhe ei aiheuta ongelmia, mutta ohjelmoijan omien tyyppien tapauksessa virheellinen tuhoaminen aiheuttaa erilaisia ongelmia, kuten purkajien kutsumatta jättämisiä tai ohjelman kaatumisen. Tutnew tarkastaa muistin vapauttamisen yhteydessä että käytetty delete on oikein tyyppinen ja antaa virheilmoituksen, jos näin ei ole. Tutnew kerää tilastoa kaikista muistinvarauksista ja vapauttamisista. Tämän tilastotiedon voi tulostaa joko itse ohjelmasta käsin funktiokutsulla tai ohjelman lopussa sopivan esikääntäjäsymbolin tai ympäristömuuttujan määrittelemällä. Jos käytetyn kääntäjän kirjastot ovat standardin mukaisia, tulostaa Tutnew myös tilaston kirjastojen (ja muiden Tutnew tä käyttämättömien ohjelman osien) dynaamisesta muistinkulutuksesta erikseen. Näitä lukuja ei ole sisällytetty käyttäjän tilastoon, vaan se sisältää ainoastaan ne muistinvaraukset, jotka on tehty Tutnew tä käyttävissä kooditiedostoissa. Valitettavasti nykyiset GCC:n (2.95.x ja 3.0) kirjastot perustuvat Silicon Graphicsin STL-toteutukseen, jonka muistinvaraus on epästandardi, eikä Tutnew näin ollen voi pitää siitä tilastoa. Tämä vika tullaan ilmeisesti korjaamaan tulevissa versioissa.

54 Harjoitustöiden laadun kehittäminen Lähes kaikkialla ohjelmoinnin perusopetuksessa korostetaan, että dynaamista muistia varattaessa tulee varautua siihen, että muistia ei saadakaan varattua. Usein muistin loppumiseen varautuminen jää kuitenkin teorian asteelle. Ohjelman testaaminen tämän virhetyypin osalta on hankalaa, koska nykyisissä tietokoneissa on yleensä niin paljon muistia (sekä fyysistä muistia että virtuaalimuistia), että sen kuluttaminen loppuun on vaikeaa. Lisäksi muistin todella loppuessa myös testaustyökalujen yms. käyttö voi olla vaikeaa. Tämän vuoksi Tutnew tarjoaa mahdollisuuden rajoittaa ohjelman käytössä olevaa dynaamisen muistin määrää ja näin simuloida muistin loppumista. Muistin loppumiseen reagoimiseen löytyy ohjeita esim. kirjasta Effective C++. Ehkä tavallisin tapa simuloida muistin loppumista Tutnew ssä on rajoittaa ohjelman käytössä olevan dynaamisen muistin määrä sopivaan pieneen arvoon. Tämän voi tehdä joko ympäristömuuttujalla, käännösaikana tai kesken ohjelman suorituksen ohjelmasta käsin. Rajoituksen voi kytkeä joko vain Tutnew n alaisuudessa tehdyille muistinvarauksille tai koko ohjelman dynaamiselle muistille. Ikävä kyllä Tutnew pystyy jälleen rajoittamaan vain C++:n new- ja operator new -operaattoreilla varatun muistin määrää, joten muistin loppumista GCC:n versioiden 2.95.x ja 3.0 STL:n sisällä ei voi simuloida. Kun muisti loppuu, tulostaa Tutnew oletusarvoisesti varoituksen, jossa se kertoo, minkä new-operaation muistinvaraus epäonnistui. Lisäksi Tutnew tulostaa testauksen helpottamiseksi muistitilaston. Tämän jälkeen ohjelman suoritus jatkuu C++:n sääntöjen mukaan ikään kuin koneen muistin olisi loppunut. Jos varoitusta muistin loppumisesta ei haluta, sen voi kytkeä pois päältä. Joskus saattaa tulla tarve testata muistin loppumisen vaikutusta juuri tietyssä vaiheessa ohjelmaa. Tällöin voi olla vaikea laskea, kuinka paljon ohjelmalle on sallittava muistia, jotta se loppuisi juuri oikeassa kohtaa. Tutnew ssä on mahdollista asettaa raja myös sille, kuinka monen muistinvarauksen jälkeen muistin loppumista simuloidaan. Rajoitus kytketään päälle aiempien rajoitusten tapaan joko ympäristömuuttujalla, esikääntäjäsymbolilla tai funktiokutsulla. Tutnew antaa myös mahdollisuuden simuloida muistin loppumista satunnaislukugeneraattorin avulla. Tällä tavoin on mahdollista testata muistin loppumista satunnaisessa ohjelman kohdassa. Muistin loppumisen todennäköisyys on mahdollista asettaa aiempien rajoitusten tapaan joko ympäristömuuttujalla, esikääntäjäsymbolilla tai funktiokutsulla. Arvona näille annetaan liukuluku väliltä 0.0 1.0. Arvo 0.0 tarkoittaa, että todennäköisyyssimulointia ei käytetä, 1.0 taas että muisti loppuu joka varauksella varmasti. Täten esim. arvo 0.1 tarkoittaa, että muisti loppuu satunnaisesti keskimäärin joka kymmenennellä varauksella. Jotta tämä simulointi olisi toistettavissa, käyttää Tutnew joka ohjelman ajokerralla samoja valesatunnaislukuja, joten muisti loppuu joka kerralla samoissa kohdissa.

Harjoitustöiden laadun kehittäminen 55 Käytettäviä valesatunnaislukuja voi vaihtaa asettamalla satunnaislukugeneraattorin siemenluku halutuksi. Tutnew käyttää valesatunnaislukujen tuottamiseen omaa valesatunnaislukugeneraattoriaan, joten tämän ominaisuuden käyttö ei sotke minkään kirjaston tuottamia valesatunnaislukuja. Muistin loppumisesta aiheutuva C++:n poikkeus on tietysti syytä ottaa kiinni ohjelmassa. Jos näin ei tehdä, kutsuu ohjelma funktiota std::terminate() (tai sille funktiolla std::terminate() määriteltyä korviketta). Tämän funktion täytyy keskeyttää ohjelman suoritus. Tutnew valvoo, että muistin loppumisesta aiheutuva poikkeus todella otetaan kiinni. Jos näin ei käy, eikä ohjelma ole määritellyt omaa korvikettaan funktiolle std::terminate(), tulostaa Tutnew virheilmoituksen. Tutnew tarkkailee myös käyttäjän std::terminate():lle mahdollisesti määrittelemää korviketta ja valvoo, että se tosiaan lopettaa ohjelman suorituksen. Jos näin ei käy, seuraa jälleen virheilmoitus. Samalla tavoin Tutnew käyttäytyy, jos muistin loppumisesta aiheutuva poikkeus ei pääse vuotamaan funktiosta ulos funktion poikkeusmääreen vuoksi. Tällöin ohjelma kutsuu funktiota std::unexpected() tai sille funktiolla std::unexpected() annettua korviketta. Tutnew tarkkailee näiden käyttäytymistä samoin kuin edellä std::terminate():n tapauksessa. 4.2.4 Yhteenveto Tutnew on standardia C++:aa käyttäen kirjoitettu kirjasto, joka tarkkailee ohjelman dynaamisen muistin käyttöä ja pyrkii löytämään ja raportoimaan siinä tapahtuvat virheet. Tutnew n käyttö on erittäin yksinkertaista, joten se sopii erinomaisesti alkeisohjelmointikursseilla käytettäväksi, mutta siitä on hyötyä kaikessa C++-ohjelmoinnissa. Tutnew n eri versioita on käytetty Tampereen teknillisellä korkeakoululla harjoitustöissä useita vuosia. Tutnew n avulla on saatu mahdolliseksi opettaa opiskelijoita kiinnittämään systemaattisesti huomiota muistinhallintaan liittyviin asioihin. Kyseessä on asia, joka tuottaa ongelmia kaikille C++-kielellä ohjelmointitektävissä toimiville, ja tämän kirjaston avulla on voitu lisätä opetusohjelmaan asian opettaminen ja vaatiminen ohjelmointikursseilla. Muistinhallinnan asioiden lisäksi tutnew:n avulla on havaittu muutamia kertoja myös hyvin vaikeasti löydettäviä alustamattomista muuttujista johtuneita virhetilanteita. Kirjaston käyttö ei ole yksinkertaisuutensa vuoksi tuottanut opiskelijoille erityisiä ongelmia, ongelmat tulevat vastaan lähinnä siinä, kun huomataan tarpeelliseksi etsiä omasta ohjelmasta syy Tutnew n antamaan virheilmoitukseen. Sen jälkeen kun Tutnew on otettu mukaan opetuksessa sekä vapaasti opiskelijoille tarjotta-

56 Harjoitustöiden laadun kehittäminen vaksi työkaluksi että opetushenkilökunnan tarkastustyötä helpottavaksi välineeksi, ohjelmointiharjoitustöiden laatu on noussut selvästi. Tutnew tä kerran käyttäneet todennäköisesti myös käyttävät kirjastoa myöhemminkin kaikessa C++-ohjelmoinnissaan havaittuaan, kuinka helposti muistinhallintavirheitä ohjelmaan jää. 4.3 Automaattinen tarkastustyökalu Ceilidh Kirsti Ala-Mutka 4.3.1 Tausta Perusohjelmointitaitojen oppiminen jo opintojen alkuvaiheessa on tietotekniikan opiskelijalle erittäin tärkeää, koska useimmilla jatkokursseilla tarvitaan ja oletetaan perusohjelmointitaitojen hallintaa. Tämän vuoksi ensimmäisillä ohjelmointikursseilla pyritään opettamaan yleisiä hyviä ohjelmointitapoja sekä tukemaan opetusta käytännön harjoitustöillä. Koska ohjelmointi on taito, jonka oppii vain käytännössä itse kokeilemalla ja tekemällä, käytännön harjoitustyöt ovat olennainen osa opetusta. Esimerkiksi Berglundin ja Danielsin Uppsalan yliopistossa suorittama tutkimus [2] osoitti, että opiskelijoiden oppimistulokset tietorakenteiden ja algoritmien kurssilla paranivat huomattavasti, kun opiskelijoiden käytännön harjoittelua lisättiin viikoittain pakollisilla harjoitustehtävillä. Harjoitustyöt ovat kuitenkin usein eniten henkilökunnan työtä vaativa osa kurssin järjestelyistä. Koska harjoitustöiden tarkastaminen on työlästä ja aikaavievää, palautteen saaminen kestää kauan ja on usein niukkaa, rajoitettu mahdollisesti vain tietoon hyväksynnästä tai hylkäyksestä. Mikäli opiskelija kuitenkin saa vasta kurssin loppuvaiheessa tiedon virheistä, joita hän teki ensimmäisessä harjoitustyössään, hän on jo käyttänyt virheellisiä käsityksiään pohjana muille opituille asioille ja ehkä ehtinyt tehdä käsitystensä perusteella seuraavatkin palautettavat työt. Pahimmassa tapauksessa opiskelijan koko kurssin suoritus keskeytyy, koska virheitä ei oikaistu ajoissa. Esimerkiksi Rekkedal osoitti jo aikaa sitten, että kun harjoitustyöpalautteen saamiseen kulunut aika lyheni 8,2 päivästä 5,6 päivään, kurssin loppuun asti suorittaneiden opiskelijoiden määrä kasvoi 69 prosentista 91 prosenttiin [3]. Myös yhdenmukainen harjoitustöiden arvostelu on vaikea, lähes mahdoton tehtävä. Useiden satojen arvosteltavien töiden käsittelyssä tarvitaan useampi kuin yksi henkilö ja siksi on vaikea taata kaikille töille täsmälleen samat arvosteluperusteet. Ongelmaa helpottavat selkeät yhdenmukaiset arvostelusäännöt. Niistäkään ei silti saada kaikkia mahdollisia tilanteita kattavia ohjeita, jolloin osa arvostelusta jää joka tapauksessa arvostelua suorittavan henkilön harkinnan varaan. Koska tarkastajia on useita, on myös vaikea muodostaa kokonaiskuvaa