A gdb-ba történő hibakeresés, hogyan lehet elolvasni egy változóhoz vagy egy osztály kívánt tulajdonságához való hivatkozást (cikkek -

A gdb-ba történő hibakeresés, hogyan lehet elolvasni egy változóhoz vagy egy osztály kívánt tulajdonságához való hivatkozást (cikkek -
Nemrégiben debütáltam egy nagy projektet, amely nagy mennyiségű szar kódot tartalmazott. A hibakeresés azért merült fel, mert egy disztribúcióban (Debian Lenny) ez a gavnocode megfelelően működött. És egy másik terjesztésben (Debian Squeeze), ugyanaz a gavnocode viselkedett teljesen másképp.

A program használata kdbg (a felület a debugger gdb), azt követte a logikáját a programot, és megállapította, hogy bizonyos ponton védett, néhány tulajdonságát egyik objektum kezdenek tartalmazhatnak érvénytelen értékeket, és ezáltal torzított logika. Ezeknek a tulajdonságoknak nincs beállítója a nebylo kódban. Az ingatlan értéke mutatott bizonyos körülmények között az osztályban, és a lehetséges értékeket szigorúan korlátozott. És azok az értékek, amelyeket megfigyeltem, nagyon különböznek a lehetségesektől.

Problémás volt, hogy egyes kódok adatokat írtak a helytelen memóriaterületen. Általában ez akkor történik, ha elhagyod a tömböt.

Sajnos az interneten található meglévő anyagok kevéssé mondják el, hogyan kell hibakeresni az osztályokat és tulajdonságokat tartalmazó gdb C ++ programokban. Ezért meg kellett érteni a meg nem éretteket.

A kdbg környezetben nézõpontokat lehet beállítani, de valójában nem kdbg-ban dolgoznak. A hibakeresést a gdb konzolban kellett végrehajtani. mert az ellenőrzési pontok a várt módon működnek. Feltételezzük, hogy az olvasó ismeri a gdb hibakeresés alapjait. tudja meghatározni a töréspontokat, végrehajtani a programot lépésekben, és tudja megnézni a változók értékeit.

Tehát a tervezőben vagyunk. Segítünk abban, hogy ez az érték bármelyik konstruktorban ismert, mivel az osztály létrehozott példányára vonatkozó memóriát mindig előre kiosztják.

A "tiszta" gdb-ben meg kell adnia a parancsot:

> print this.counter

Ezekkel a parancsokkal megtanultam a változó értékét, és a kdbg esetében a név helyes írásmódját.

> nyomtatás (This.counter)

Általában véve észrevettem, hogy a "tiszta" gdb-ben nem feltétlenül kell a C / C ++ szintaxisban kifejezett kifejezéseket írni. Például a "->" karakterek helyettesíthetők a ".", És a "*" írása a mutató típusáról való tájékoztatáshoz szintén opcionális. Ez egyszerűsíti a hibakeresést.

Egy tulajdonság lehet egy mutató egy alap típusú vagy mutatóhoz egy másik objektumhoz (az alábbiakban tárgyaljuk). Ebben az esetben a cselekvések megegyeznek! Egyszerűen, ha a tervezőben van, akkor szükség van rá, ha a mutatót inicializálni szeretné. Ezután kattintson rá a jobb egérgombbal, válassza a "Watch expression" lehetőséget. A megfigyelések területén egy sor jelenik meg:

Ez a sor bővíthető, és benne van a mutató által hivatkozott érték.

Gyakran előfordul, hogy egy probléma-változó egy olyan tulajdonságban van, amely egy objektum. És már ebben az objektumban probléma van.

Ebben az esetben lépésről lépésre meg kell kezdeni az objektum tulajdonságának inicializálását. Ezután a "változó" fát fel lehet bővíteni a kívánt változóra, és ugyanúgy a jobb egérgombbal válaszd a "Watch expression" kifejezést. A megfigyelések területén egy sor jelenik meg:

A "tiszta" gdb-ben ugyanazon műveleteknél megadhatja a parancsot:

> print this.window.active

+-> (bool *) aktív 0x859f161

Ezenkívül feltételezzük, hogy a hibakeresés a gdb-hez érkezik, mivel a kdbg nézõpontok vannak, de nem működnek.

Azt feltételezik, hogy van egy osztály változó Stapp pWindow típus „mutatót az objektum STWindow”, és ennek az objektumnak STWindow bool változó bSleep. Ennek a változónak az értéke egy ponton elkényeztetett. Meg kell fogni a pillanatot, és nézd meg a kódot, hogy meghamisítja az adatokat.

A töréspontot az STApp osztály kivitelezőjének első utasítására helyezzük. Futtassa a programot végrehajtásra. A lépésenkénti leállítás után elérjük azt a pontot, amelyen a pWindow objektumot inicializáljuk. Ezután győződjön meg arról, hogy az inicializálás rendben volt:

$ 1 = (STApp * const) 0xbffff284

(gdb) kinyomtatja ezt

$ 2 = (STWindow * osztály) 0x859ee10

(gdb) kinyomtatja ezt az ablakot

A forráskód alapján ítélve a bSleep változó kezdetben hamis. így rendben van.

(gdb) nyomtatás (This.pWindow.bSleep)

$ 4 = (bool *) 0x859f161

(gdb) nézni * ((int *) 0x859f161)

2. hardver figyelőpont: * ((int *) 0x859f161)

Ellenőrizzük, hogy ezt az órát hogyan adtuk hozzá:

Num Type Disp Enb Cím Mi

1 breakpoint megtartja az y 0x08070fa8 értéket a STApp :: STApp (int, char **)

a töréspont már 1 alkalommal ütközött

2 hw figyelőpontot tartani y * ((int *) 0x859f161)

A program két megszakítását látjuk: az első megszakítás egy normál töréspont, amely nekünk dolgozott. A második megszakítás az éppen létrehozott hardveres figyelőpont.

Amikor az óra működött

Miután beállította az órát, a folytatás parancsot adjuk meg. így a program folytatja a végrehajtását. És egy bizonyos ponton a gdb leállítja a program végrehajtását:

2. hardver figyelőpont: * ((int *) 0x859f161)

Régi érték = 1409833472

Új érték = 1413336268

gombra :: számítás (ez = 0x859f160, deltaT = 0.100000001)

14 fLastPressed + = deltaT;

Lássuk, mi a kód a 14. sor mellett:

üres gomb: számítás (float deltaT)

Aha, nyilvánvalóan, az osztály gomb egy bizonyos objektuma valahol a bSleep közelében található. és fLastPressed tárolásakor. írja ezt az értéket, ahol a bSleep fekszik. Lássuk a híváscsomagot:

# 0 gomb :: számít (ez = 0x859f160, deltaT = 0.100000001)
a /St/Window/button.cpp:14 mappában

# 1 0xb644d5a3 a STBaseWindow :: compute (ez = 0x859ee10, deltaT = 0,100000001)
a /St/Window/stWindowsSDL.cpp:423 címen

# 2 0xb642d2b6 az STSceneGraph :: render_scene (ez = 0xb1fda008)
a /St/Render/SceneGraph/stSceneGraph.cpp:412 könyvtárban

# 3 0xb6437106 az STSceneGraphSlot :: render_scene (ez = 0xb1fda008)
a /St/Render/SceneGraph/stSceneGraphSlot.cpp:138 címen

# 4 0x08070b82 a STApp :: Idle (ez = 0xbffff284)
a /St.2.0/Game/stApp.cpp:39 címen

# 5 0xb644db65 a STBaseWindow :: MainLoop (ez = 0x859ee10)
a /St/Window/stWindowsSDL.cpp:275 címen

# 6 0xb63ff42c az App :: hurokban (ez = 0xbffff284)
a /St/Application/app.cpp:49 oldalon

# 7 0x08071c71 a STApp :: Run-ban (ez = 0xbffff284, argc = 1, argv = 0xbffff364)
a /St.2.0/Game/stApp.cpp:495 címen

# 8 0x08073280 fő (argc = 1, argv = 0xbffff364)
a /St.2.0/Game/stMain.cpp:13 címen

Nézzük meg a kódot, amely a button :: compute () metódust hívja. a hívásstallumból származó információk szerint itt található: /St/Window/stWindowsSDL.cpp:423. Ez a kód így néz ki:

az (int c = 0; c<25;c++ )
dEmulattionButton [c] .compute (deltaT);
dButtons [c] .compute (deltaT);
>

Aha, tárgyak tömbjei. És a "mágikus állandó" 25. És mi a dimenziója ezeknek a tömböknek? A /St/Window/stWindowsSDL.h fejlécfájlban megtaláljuk a következő kódot:


gomb dButtons [24];
gomb dEmulattionButton [24];
.

Ismét a "mágikus konstans", de ezúttal 24. Azt hiszem, minden világos - egy klasszikus barlang van mágikus konstansokkal és egy kiút a tömbből.

Így megtudtuk, mi a probléma, és nem lenne nehéz megoldani.

A Linuxban egy furcsa hagyomány szerint a programok neve gyakran emberileg olvasható. Ezért felhívom a figyelmet arra, hogy a konzol hibakeresőjét gdb - G nu D eB uggernek hívják. És a grafikus KDE-felületet kdbg - K de D eB ugG ernek hívják. Ez azt jelenti, hogy a gdb elején a g nu jelentése G, és a végén a kdbg jelenti a G er utótagot. Ne tévessze össze az írást.

Minden jó szerencsét és hibás programokat.