Depanarea în gdb cum să capturați referința la o variabilă sau la proprietatea dorită a unei clase (articole -

Depanarea în gdb cum să capturați referința la o variabilă sau la proprietatea dorită a unei clase (articole -
Recent am avut de a depana un proiect major care conținea bucăți mari de cod de rahat. Necesitatea de depanare a apărut datorită faptului că într-o distribuție (Debian Lenny) acest gavnocode a funcționat corect. Și într-o altă distribuție (Debian Squeeze), același gavnocode sa comportat destul de diferit.







Folosind programul kdbg (interfața pe GdB depanator), am urmat logica programului și a constatat că, în unele, mai multe proprietăți protejate puncte ale unuia dintre obiectele încep să conțină valori incorecte și, prin urmare, logica distorsionat. Nu există setteri pentru aceste proprietăți în codul neblo. Valorile proprietăților au fost expuse pentru anumite condiții în clasa însăși, iar intervalul de valori posibile a fost foarte limitat. Iar acele valori pe care le-am observat erau foarte diferite de cele posibile.

Problema a fost că unele coduri au scris date în zona greșită a memoriei. De obicei, acest lucru se întâmplă atunci când părăsiți matricea.

Din păcate, materialele existente de pe Internet nu oferă prea multe informații despre modul de depanare în programele GDB C ++ care conțin clase și proprietăți. Prin urmare, a fost necesar să înțelegem ce a trecut.

În mediul kdbg, este posibil să setați puncte de vizitare, dar de fapt nu funcționează în kdbg. Depanarea trebuia făcută în consolă gdb. pentru că în acest punct de lucru funcționează cum era de așteptat. Se presupune că cititorul este familiarizat cu fundamentele depanării în gdb. știe cum să stabilească puncte de întrerupere, să execute programul în pași și știe cum să vizualizeze valorile variabilelor.

Deci, suntem în proiectant. Vom fi ajutați de faptul că această valoare este cunoscută în orice constructor, deoarece memoria pentru instanța creată a clasei este întotdeauna alocată în avans.

În gdb-ul "curat" trebuie să dați comanda:

> imprimați acest.counter

Cu aceste comenzi, am aflat valoarea variabilei și, în cazul kdbg, corectarea ortografică a numelui.

> imprimare (This.counter)

În general, am observat că în gdb "pur" nu este necesar să scrieți expresiile în sintaxa C / C ++ absolut corect. De exemplu, caracterele "->" pot fi înlocuite cu ".", Iar scrierea "*" pentru a informa despre tipul de pointer este, de asemenea, opțională. Acest lucru simplifică depanarea.

O proprietate poate fi un pointer la un tip de bază sau un pointer către un alt obiect (discutat mai jos). În acest caz, acțiunile sunt la fel! Pur și simplu, fiind în proiectant, este necesar să se doneze până la locul unde se pornește cursorul. Apoi, faceți clic pe acesta cu butonul din dreapta al mouse-ului, selectați "Expresați-vă". O linie va apărea în câmpul observațiilor:

Această linie poate fi extinsă, iar în ea puteți vedea valoarea la care se referă acest indicator.

Se întâmplă de multe ori că o variabilă de problemă se află într-o proprietate care este un obiect. Și deja în acest obiect există o proprietate problemă.







În acest caz, trebuie să parcurgeți pas cu pas inițializarea proprietății obiectului. Apoi, puteți "extinde" arborele de variabile la variabila de interes, și în același mod pe butonul din dreapta al mouse-ului alegeți "Expresia ceasului". O linie va apărea în câmpul observațiilor:

În gdb "curat" pentru aceleași acțiuni, puteți da comanda:

> imprimați acest.window.activ

+-> (bool *) activă 0x859f161

Mai mult, se presupune că depanarea merge la gdb, deoarece punctele de vizitare kdbg sunt acolo, dar nu funcționează.

Se presupune că există o clasă variabilă de tip Stapp pWindow „pointer la STWindow obiect“, iar acest obiect are STWindow bool este un bSleep variabilă. Valoarea acestei variabile la un moment dat este răsfățată. Trebuie să surprindem acest moment și să examinăm codul care corupe datele.

Am pus punctul de întrerupere la prima instrucțiune din constructorul de clasă STApp. Rulați programul pentru execuție. După oprirea pas-cu-pas ajungem la punctul în care obiectul pWindow este inițializat. Apoi, asigurați-vă că inițializarea a fost corectă:

$ 1 = (STApp * const) 0xbffff284

(gdb) print this.pWindow

$ 2 = (clasa STWindow *) 0x859ee10

(gdb) print this.pWindow.bSleep

Judecând după codul sursă, variabila bSleep trebuie să fie inițial falsă. așa că e în regulă.

(gdb) (This.pWindow.bSleep)

$ 4 = (bool *) 0x859f161

(gdb) ceas * ((int *) 0x859f161)

Poziția hardware 2: * ((int *) 0x859f161)

Verificăm cum a fost adăugat acest punct de vizionare:

Tip Num Disp

1 punct de întrerupere păstrează y 0x08070fa8 în STApp :: STApp (int, char **)

punctul de lovire a lovit deja 1 dată

2 hw watchpoint păstra y * ((int *) 0x859f161)

Vedem două întreruperi ale programului: prima întrerupere este o întrerupere normală, care a funcționat pentru noi. A doua întrerupere este punctul de supraveghere hardware pe care tocmai l-am creat.

Când paznicul a funcționat

După ce este setat ceasul, vom da comanda continuă. astfel încât programul să-și continue executarea. Iar la un moment dat, gdb va opri executarea programului:

Poziția hardware 2: * ((int *) 0x859f161)

Valoarea veche = 1409833472

Valoare nouă = 1413336268

butonul :: calcula (aceasta = 0x859f160, deltaT = 0.100000001)

14 fLastPressed + = deltaT;

Să vedem ce cod este lângă această linia a 14-a:

buton void :: compute (float deltaT)

Aha, aparent, un anumit obiect al butonului de clasă este situat undeva în apropierea proprietății bSleep. și atunci când stocați fLastPressed. scrie această valoare unde se află bSleep. Să vedem stiva de apeluri:

Butonul # 0 :: calculați (acest = 0x859f160, deltaT = 0.100000001)
la /St/Window/button.cpp:14

# 1 0xb644d5a3 în STBaseWindow :: calculați (aceasta = 0x859ee10, deltaT = 0.100000001)
la /St/Window/stWindowsSDL.cpp:423

# 2 0xb642d2b6 în STSceneGraph :: render_scene (aceasta = 0xb1fda008)
la /St/Render/SceneGraph/stSceneGraph.cpp:412

# 3 0xb6437106 în STSceneGraphSlot :: render_scene (this = 0xb1fda008)
la /St/Render/SceneGraph/stSceneGraphSlot.cpp:138

# 4 0x08070b82 în STApp :: Idle (acest = 0xbffff284)
la /St.2.0/Game/stApp.cpp:39

# 5 0xb644db65 în STBaseWindow :: MainLoop (aceasta = 0x859ee10)
la /St/Window/stWindowsSDL.cpp:275

# 6 0xb63ff42c în App :: loop (this = 0xbffff284)
la /St/Application/app.cpp:49

# 7 0x08071c71 în Stapp :: Run (acest = 0xbffff284, argc = 1, argv = 0xbffff364)
la /St.2.0/Game/stApp.cpp:495

# 8 0x08073280 în principal (argc = 1, argv = 0xbffff364)
la /St.2.0/Game/stMain.cpp:13

Să ne uităm la codul care apelează metoda button :: calculate (). judecând după informațiile din stiva de apel, se află aici: /St/Window/stWindowsSDL.cpp:423. Acest cod arată astfel:

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

Aha, o serie de obiecte. Și "constanta magică" 25. Și care este dimensiunea acestor tablouri? Am găsit următorul cod în fișierul de antet /St/Window/stWindowsSDL.h:


butonul dButtons [24];
butonul dEmulattionButton [24];
.

Din nou, "constanta magică", dar de data asta 24. Cred că totul este clar - avem o cavernă clasică cu constante magice și o ieșire din matrice.

Astfel, am aflat care a fost problema și nu ar fi greu să o rezolvăm.

În Linux, printr-o tradiție ciudată, numele programelor sunt adesea citite de oameni. Prin urmare, vă atrag atenția asupra faptului că depanatorul consolei este numit gdb - G nu D eB ugger. Și interfața grafică KDE la aceasta se numește kdbg - K de D eB ugG er. Adică, litera g la începutul lui gdb înseamnă G nu, iar la sfârșit kdbg înseamnă sufixul G er. Nu confunda scrisul.

Toate programele de noroc și buggy.







Trimiteți-le prietenilor: