Vissza az előzőleg látogatott oldalra (nem elérhető funkció)Vissza a modul kezdőlapjáraUgrás a tananyag előző oldalára (25)Ugrás a tananyag következő oldalára (27)Fogalom megjelenítés (nem elérhető funckió)Fogalmak listája (nem elérhető funkció)Oldal nyomtatása (nem elérhető funkció)Oldaltérkép megtekintéseSúgó megtekintése

Tanulási útmutató

Összefoglalás

Ebben a leckében a programozással kapcsolatos, az eddigi lépéseknél kevésbé „kreatív”, ámde nem nélkülözhető gyakorlati ismeretek közül a hibakereséssel ismerkedünk meg.

Követelmény

Önállóan megoldható feladatok

Hibakeresés és javítás

1. Hibajelenségek

Az előző fejezetekben a hibafelderítéssel foglalkoztunk, ennek a fejezetnek a tárgya a hiba behatárolása, a hiba kijavítása.

Első lépésként tekintsük át, hogy milyen hibajelenségekkel találkozhatunk, és észlelésükkor mi a teendő!

1.1. Szintaktikus hiba

Csak értelmezés (interpretálás) esetén fordul elő, hiszen a fordítóprogramok korábban még a fordítási fázisban kijelzik, s nem is készül el a futtatható változat.

Ennél a hibánál nincs különösebb teendőnk, hiszen a hibajelenség a hibás utasításnál (vagy annak „környékén”) jelentkezik, csak ki kell javítanunk.

1.2. Végrehajtási hiba

Futás során a fordítóprogram által beépített hibafigyelő eljárás által adott hiba, például nullával osztás, túlcsordulás stb. Az ilyen hibajelenségek közös jellemzője, hogy a leállás sorában szerepel a – rossz értéke miatt – hibát okozó változó, és meg kell keresni, hogy értéke hol romlott el.

Megjegyzés

Abból indultunk ki, hogy olyan programfejlesztői környezetben keressük a hibát, amelyben kiderül, hogy a forrásprogram mely utasítássoránál következett be a hiba. (Ellenkező esetben csak egy nem túl sokat mondó memóriacím és egy hibakód kettőse áll rendelkezésünkre.) Ez a feltevésünk természetesen igaz a továbbiakban is.

1.3. Nem áll le a program

Végtelen ciklusra utaló jelenség, a program láthatólag semmit nem csinál, mégsem fejeződik be. Rosszabb, ha valamit eközben tesz is (képernyőre ír, fájlba ír stb.)

Ekkor a program külső leállításakor általában meg lehet tudni az aktuálisan végrehajtott utasítást. Innen kiindulva meg kell keresni azt a ciklust, amelyik nem fejeződik be, majd meg kell találnunk e nem befejeződés okát (azaz a ciklusfeltétel „változatlanságának” okát).

Sajnos nem biztos, hogy a jelenség hiba következménye, lehet ugyanis, hogy csak túl lassú a programunk, és nem győztük kivárni a befejeződését. (Erre a hatékonysággal összefüggő kérdésre egy későbbi fejezetben még visszatérünk.)

1.4. Nem ír ki semmit a program, illetve „részlegesen” ír ki

A program futása leáll, és nem látunk semmilyen eredményt, vagy a várt eredmények közül csak egyeseket látunk.

Ebben az esetben vagy elfelejtettünk valamilyen kiíró utasítást tenni a programba, vagy rossz helyre írjuk ki az eredményt (pl. nem abba a fájlba, mint ahova kellene), vagy pedig a kiírások ugyan szerepelnek a programban, de egy olyan ágon, amelyet nem mindig vagy nem a kellő pillanatban hajtunk végre.

Bármelyik esetről is van szó, teendőnk a kiíró utasítások végigbogarászása, kijavítása vagy jó helyre tétele.

1.5. Rosszat ír ki a program

Hasonló a helyzet a futási hibák esetéhez, itt is ugyanaz a teendő, mint amit ott már részleteztünk.

A hibakeresés akkor kezdődik, ha egy hibajelenséget tapasztaltunk, és célja a hiba helyének megtalálása. Először módszereket fogunk áttekinteni, majd megnézzük, hogy ehhez milyen eszközök állnak rendelkezésünkre.

Ha a hibakeresés véget ért, akkor kezdődik a hibajavítás.

E két résztevékenységre vonatkozóan a tesztelés alapelveihez hasonlóan megfogalmazhatunk néhány alapelvet.

Vissza a tartalomjegyzékhez

2. A hibakeresés alapelvei

Vissza a tartalomjegyzékhez

3. A hibajavítás alapelvei

Vissza a tartalomjegyzékhez

4. Hibakeresési módszerek

A hibakeresési módszerek kétféle kérdésre adhatnak választ:

Ehhez jön a hibajavítás:

Először a hibaosztályt azonosítjuk, azaz arra a kérdésre keressük a választ, hogy milyen adatfélékre működik helytelenül a programunk.

4.1. Indukciós módszer

Indukció [Új Magyar Lexikon]: Abból a tényből, hogy nagyszámú tárgynak meghatározott tulajdonsága van, és közös nemhez tartozik, arra következtetünk, hogy az adott nemhez tartozó összes tárgynak megvan ez az ismertetőjegye.

A hibakeresést a következőképpen végezzük: kiindulunk a rendelkezésre álló teszteset-eredményekből, majd megpróbáljuk azokat rendezni. Célszerű megvizsgálni az olyan teszteseteket is , amelyek nem idézik elő az adott hibát. A rendezett adatokból megpróbálunk valamilyen feltevést tenni a hiba okára vonatkozóan.

A legelső feltevés, hogy a program csak azokra az esetekre hibás, amelyeket már kipróbáltunk, és rájuk rossz eredményt kaptunk. Ha ezt a feltevést igazolni tudjuk, akkor következhet a hiba lokalizálása, majd kijavítása, egyébként a hibás bemenő adatok körét próbáljuk meg – fokozatosan – bővíteni újabb teszteléssel! Igazoljuk az egyre bővülő adathalmazra a program hibás működését mindaddig, amíg csak lehet.

Végül eljutunk a bemenő adatoknak ahhoz a halmazához, amelyre a program hibás, és egy másikhoz, amelyre pedig helyes. Ezután meg kell állapítanunk, hogy a programon átvezető tesztutak mely ágai azok, amelyek a hibás tesztpredikátumnak megfelelnek, és melyek azok, amelyek nem. Azokon a programágakon kell keresni a hibát, amelyek csak a hibás teszt-predikátumhoz tartoznak.

Példa

X=0, 1, 5, 100 – a program jól működik

X=-1, -7, -50 – rosszul működik

→ feltevés: a program a negatív számokra működik hibásan.

4.2. Dedukciós módszer

Dedukció [Új Magyar Lexikon]: Abból az ítéletből, hogy az adott nemhez tartozó összes tárgy meghatározott ismertetőjeggyel rendelkezik, arra következtetünk, hogy bizonyos, az adott nemhez tartozó tárgyak szintén rendelkeznek a szóban forgó ismertetőjeggyel.

A módszer lényege az, hogy egyre szűkíti a hiba lehetséges okainak körét. A meglévő teszteset-eredményekből adódó mindenféle lehetséges okot fel kell tételezni az első lépésben, majd ezek közül ki kell küszöbölni azokat, amelyek a részletesebb vizsgálat során nem állják meg a helyüket. Ha egy feltevést teszünk, ugyanúgy igazolnunk kell, mint az előző módszer esetén. Ha nem sikerül, akkor újabb információkat kell gyűjtenünk a hibakereséshez a hiba-jelenségről.

Ha elérkeztünk a hibás adatok köréhez, akkor már ugyanaz a teendőnk, mint az előbbi esetben volt.

Példa

feltevés: a program mindig rosszul működik

teszt: X=3, 5, 7 – rossz eredmény

X=12, 20 – jó eredmény

→ feltevések:

• X= prímszám → rossz

• X= páratlan → rossz

• X<10 → rossz

• 4 nem osztója X-nek → rossz

Ha egy feltevés igaz, akkor az általa kijelölt halmaz (ekvivalenciaosztály) minden egyes tagjára rosszul fog működni a program.

Ha a hiba természetét kiderítettük, akkor következhet a lokalizálás, a hibahelykeresés.

4.3. Visszalépéses technika – hibahelykeresés „elvben”

A legismertebb hibakeresési módszer, amelyet úgy végzünk el, hogy kiindulunk a hiba előfordulásának helyéről, és a programot „visszafelé” hajtjuk végre mindaddig, míg a végrehajtás eredményét hibásnak találjuk. Ha elérkeztünk egy olyan ponthoz a programban, ahol a hibás eredmények után helyes eredményeket kapunk, akkor valószínűleg megtaláltuk a hiba forrását.

A visszalépéses technikát sokszor segítik hibakeresési eszközök, melyek segítségével a visszalépést nem kézzel kell elvégezni, hanem a program futása során automatikusan megtörténik.

4.4. Teszteléssel segített hibakeresés – hibahelykeresés „eszközzel”

A teszteseteket megkülönböztethetjük aszerint, hogy hibát akarunk felfedezni, vagy pedig egy ismert hibát akarunk előidézni a programban (a hiba okát keresve). Az utóbbi típusú tesztesetek szolgálnak hibakeresésre (emlékeztetünk itt a tesztesetek megismételhetőségére).

Ezeknek a teszteseteknek jellegzetessége, hogy csak egyetlen feltételt fednek le. Az első két módszer alapján még esetleg több programágon is lehet a hiba; teszteseteket úgy kell választani, hogy mindegyik tesztesethez más-más programág tartozzon!

Ezt a módszert általában nem önállóan, hanem az előző három módszer segítésére használjuk.

Vissza a tartalomjegyzékhez

5. Hibakeresési eszközök

A hibakeresési eszközök a programozási környezet olyan elemei, amelyek a hiba okának megállapítását, a hiba helyének megkeresését teszik könnyebbé azzal, hogy futás közbeni információt szolgáltatnak a programról.

Egyes eszközök alkalmazásához a programszövegbe tesztelő utasításokat kell elhelyezni, az eszközök alkalmazása után pedig eltávolítani. A gyakori hibakeresésnél azonban sokszor éppen azt kell visszaírni, amit előzőleg eltávolítottunk. Emiatt a jó fordítóprogramok működnek ún. tesztelő üzemmódban, amelyben a tesztelő utasításokat is bele kell fordítani a célprogramba, normál üzemmódban azonban nem. Ilyen esetben a tesztelő utasításokat nem kell kitörölni a programszövegből.

Az animáció bemutatja a teszteset készítés alapjait és ennek a hibakeresésben játszott szerepét:

Az animáció bemutatja azt, hogy hogyan érdemes különböző teszteket készíteni.

Flash lejátszó letöltése

Tesztesetek előállítása

5.1. Kiírás

A leggyakrabban használt eszköz alkalmas adatkiírások elhelyezése. Kétféle fajtája lehet.

Az egyikben a programszöveg bizonyos helyeire helyezünk el tesztkiírásokat. Ha a futás során arra a pontra érünk, akkor a benne szereplő változókat a program kiírja, majd várakozik a továbbindításra.

A másikban a kiírandó változókat rögzítjük, és értékük a futás során mindig látszik/megnézhető a képen. Ez utóbbi gyakran más eszközökkel (pl. töréspont) kombináltan jelenik meg.

Egy primitívebb fajtája az assembly programoknál használatos regiszter, illetve memóriakiírás.

5.2. Nyomkövetés

A nyomkövetés lényege a végrehajtott utasítások követése a programban. Itt tehát futás során az eredményképernyő mellett a programszöveget is látnunk kell. A programszövegből vagy az éppen végrehajtott utasítást látjuk, vagy a teljes programszövegben mutatja egy mutató az aktuális utasítást, vagy pedig algoritmikus struktúrák végrehajtását figyelhetjük meg.

A nyomkövetés kiterjedhet a program egészére, de megjelölhetünk programrészeket, és ilyenkor csak ezek végrehajtását kell követni. A programrész lehet a teljes program; egy adott eljárás és mindazok, akiket hív; vagy egy adott eljárás az általa hívott eljárások nélkül. Egyes esetekben pedig mi helyezhetünk el a programszövegben tetszőleges helyre nyomkövetést bekapcsoló, illetve kikapcsoló utasításokat.

A nyomkövetés általában sokféle információt adhat a program futásáról: a végrehajtott utasítás mellett kiírhatjuk a képernyőre annak hatását (értékadásnál a változóba elhelyezett értéket, elágazás- vagy ciklusfeltétel kiértékelésénél annak igaz vagy hamis értékét).

Egy szűkebb információt adó változatban csak bizonyos típusú utasításokat nyomkövetünk, például az eljárás- és függvényhívásokat.

5.3. Adatok nyomkövetése

A nyomkövetés egy speciális változatában nem az utasításokat vizsgáljuk, hanem a változókat. Ebben az esetben akkor kapunk a képernyőn üzenetet, ha a kijelölt változó(ka)t valaki használja, illetve módosítja.

Az adatok nyomkövetését könnyen megvalósíthatjuk az általunk készített típusok moduljaiban: csupán annyi a teendőnk, hogy a típust kezelő eljárásokat ellássuk megfelelő kiíró utasításokkal.

5.4. Nyomkövetés a hibától visszafelé

Ez egy olyan nyomkövetési eljárás, amely akkor lép életbe, ha a program futása hibával megszakadt (valamint ha a lépésenkénti végrehajtás során mi magunk kezdeményezzük ezt). Ekkor a végrehajtásról gyűjtött adatok alapján elindulunk a programban „visszafelé”, és látjuk mindazt, amit a normális nyomkövetésnél láttunk.

5.5. Töréspontok elhelyezése

A töréspontok a program olyan utasításai, amelyeknél a végrehajtásnak meg kell állnia, a felhasználó információt szerezhet a program állapotáról, majd folytatódhat a végrehajtás.

Egy speciális fajtája az a végtelen ciklusok felfedésére szolgáló változat, amikor a töréspontnak kijelölt utasításnak csak adott darabszámszori végrehajtása után kell a futást felfüggeszteni.

Leálláskor a felhasználó dönthet a futtatás abbahagyásáról, illetve folytatásáról. Megnézheti változók értékeit, nyomkövetést be- és kikapcsolhat, töréspontokat megszüntethet, illetve újakat definiálhat stb. Sőt némely környezet azt a nem veszélytelen lehetőséget is biztosítja, hogy egy-egy változó értékét módosítsuk, és újabb elindítás nélkül lokalizálhassunk esetleges más hibákat is. (Veszélyes, mert elvonhatja a figyelmünket az épp megtalált hibáról, illetve ha nem minden szükséges változót állítottunk megfelelő értékűre, akkor „álhibák” keletkezhetnek, vagy meglévő hibák kendőződhetnek el.)

5.6. Töréspontok elhelyezése

A töréspontok a program olyan utasításai, amelyeknél a végrehajtásnak meg kell állnia, a felhasználó információt szerezhet a program állapotáról, majd folytatódhat a végrehajtás.

Egy speciális fajtája az a végtelen ciklusok felfedésére szolgáló változat, amikor a töréspontnak kijelölt utasításnak csak adott darabszámszori végrehajtása után kell a futást felfüggeszteni.

Leálláskor a felhasználó dönthet a futtatás abbahagyásáról, illetve folytatásáról. Megnézheti változók értékeit, nyomkövetést be- és kikapcsolhat, töréspontokat megszüntethet, illetve újakat definiálhat stb. Sőt némely környezet azt a nem veszélytelen lehetőséget is biztosítja, hogy egy-egy változó értékét módosítsuk, és újabb elindítás nélkül lokalizálhassunk esetleges más hibákat is. (Veszélyes, mert elvonhatja a figyelmünket az épp megtalált hibáról, illetve ha nem minden szükséges változót állítottunk megfelelő értékűre, akkor „álhibák” keletkezhetnek, vagy meglévő hibák kendőződhetnek el.)

5.7. Lépésenkénti végrehajtás

Ez tulajdonképpen olyan eszköz, amely a program minden utasítására egy töréspontot definiál. A program minden utasításának végrehajtása után lehetőség van a töréspontoknál ismertetett beavatkozásokra.

5.8. A hiba helyének és okának kijelzése

Fordítóprogramoknak általában vannak olyan lehetőségei, hogy a futás közbeni ellenőrzéseket beépítsék a program kódjába, illetve kihagyják belőle. A kihagyás a már biztosan helyes programnál futási időt csökkentő tényező lehet, emiatt érdemes a kész programokat így lefordítani.

A befordított ellenőrzések (pl. túlcsordulás, indextúllépés) viszont elősegítik a hibajelenségek minél korábbi felismerését.

Megjegyzés

Itt kell megjegyeznünk, hogy az ilyen hibaüzenetek nem a program majdani felhasználójának szólnak, hanem a programfejlesztőnek. Programfelhasználó a nyelvi környezet hibajelzéseivel nem találkozhat! Arról is tudnunk kell, hogy minden hibafigyelő mechanizmus lassítja a programot, növeli annak méretét, tehát hatékonyság szempontjából kedvezőtlen.

5.9. Állapotellenőrzés

Ha a programot valamely formális tervezési eszközzel készítettük (levezetés, helyességbizonyítás), akkor rendelkezésünkre állnak a program állapotára vonatkozó állítások. Ilyen állításokat egyébként tetszőleges programhoz is készíthetünk. Az automatikus ellenőrző rendszer feladata annak ellenőrzése, hogy a program megfelelő pontjain ezek az állítások valóban teljesülnek-e vagy sem.

Ez az eszköz hasonlít az előzőre, de nemcsak az ún. fatális hibákat jelzi, hanem a futás közbeni teljes állapotot ellenőrzi.

Az állapotellenőrzés alkalmas lehet feltételes töréspontok, feltételes kiírásának elhelyezésére a programban.

Vissza a tartalomjegyzékhez

6. Tipikus hibák

A hibakeresést nagyban megkönnyíti az, ha tudjuk, hogy mire figyeljünk. Szép számmal vannak olyan hibák, amelyeket sokan, sokszor követnek el. A hibakeresés első lépése lehet e tipikus hibák megvizsgálása. Ebben a fejezetben ezeket soroljuk fel vázlatszerűen.

6.1. Gépelési hibák

6.2. Elágazásszervezési hibák

6.3. Ciklusszervezési hibák

6.4. Bemeneti adatok hibái

6.5. Kimeneti adatok hibái

6.6. Fájlok hibái

6.7. Változókkal kapcsolatos hibák

6.8. Kifejezések hibái

6.9. Eljárások hibái

6.10. Grafikai hibák

Vissza a tartalomjegyzékhez

Fel a lap tetejére
Új Széchenyi terv
A projekt az Európai Unió támogatásával, az Európai Szociális Alap társfinanszirozásával valósul meg.