Differences

This shows you the differences between two versions of the page.

Link to this comparison view

programare:tutoriale:debugging [2022/01/03 13:38]
cpatrascu1910 [Concluzie]
programare:tutoriale:debugging [2022/01/21 02:03] (current)
radu.nichita
Line 9: Line 9:
 **Resposabili**:​ **Resposabili**:​
   * Cristi Olaru   * Cristi Olaru
-  * Cristi Popa 
   * Cristi Pătrașcu   * Cristi Pătrașcu
 +  * Cristi Popa
   * Darius Neațu   * Darius Neațu
   * Liza Babu   * Liza Babu
Line 41: Line 41:
 De foarte multe ori, codul pe care îl scriem nu funcționează de la început așa cum ne dorim, deoarce conține cel puțin un [[https://​en.wikipedia.org/​wiki/​Software_bug|bug]]. Un bug poate să fie: De foarte multe ori, codul pe care îl scriem nu funcționează de la început așa cum ne dorim, deoarce conține cel puțin un [[https://​en.wikipedia.org/​wiki/​Software_bug|bug]]. Un bug poate să fie:
  
-  * un comportament care nu este cel dorit (e.g. nu se obține rezultatul dorit / corect) +  * un comportament care nu este cel dorit (e.g. nu se obține rezultatul dorit / corect) 
-  * când programul nu se termină (e.g. ciclează ​la infinit+  * când programul nu se termină (e.g. ciclează) 
-  * când primim o eroare fatală neașteptată (e.g. ''​%%Segmentation fault%%''​).+  * când primim o eroare fatală neașteptată (e.g. ''​%%Segmentation fault%%''​).
  
 Primul pas este să răspundem la întrebarea “De ce nu merge?” (să găsim bug-ul), lucru care de cele mai multe ori este mai dificil decât rezolvarea propriu-zisă a problemei. Primul pas este să răspundem la întrebarea “De ce nu merge?” (să găsim bug-ul), lucru care de cele mai multe ori este mai dificil decât rezolvarea propriu-zisă a problemei.
Line 49: Line 49:
 O posibilă modalitate de a descoperi bugurile programului nostru este pur și simplu să ne uităm pe cod. Dacă problema este destul de evidentă, putem avea “noroc” să observăm repede bug-ul. O posibilă modalitate de a descoperi bugurile programului nostru este pur și simplu să ne uităm pe cod. Dacă problema este destul de evidentă, putem avea “noroc” să observăm repede bug-ul.
  
-''​%%Debuggingul%%''​ este procesul prin care găsim și rezolvăm bugurile dintr-un program, skill foarte important pentru orice inginer software. Acesta constă în înțelegerea codului, formularea unor ipoteze (e.g. "​Ar putea fi o problemă în funcția ''​%%foo()%%''​ ) și validarea acestora folosind mesaje ​de log (e.g. ''​%%printf%%''​ ) sau tooluri de debug (e.g. ''​%%gdb%%''​ ).+''​%%Debuggingul%%''​ este procesul prin care găsim și rezolvăm bugurile dintr-un program, skill foarte important pentru orice inginer software. Acesta constă în înțelegerea codului, formularea unor ipoteze (e.g. "Ar putea fi o problemă în funcția ''​%%foo()%%''​ ), folosirea mesajelor ​de log (e.g. ''​%%printf%%''​ ), a unor tooluri de debug (e.g. ''​%%gdb%%''​ ), etc.
  
 ===== Debugging în practică ===== ===== Debugging în practică =====
Line 57: Line 57:
 În contextul în care o aplicație ajunge să fie folosită de un număr mare de utilizatori,​ este foarte posibil să apară și probleme care nu au fost întâmpinate în procesul de dezvoltare și testare. De asemenea, dacă singura informație disponibilă este “Ceva nu a mers cum trebuie la un utilizator”,​ este foarte greu să se rezolve un bug deoarece nu există context; nu știm în ce condiții s-a reprodus bugul. În contextul în care o aplicație ajunge să fie folosită de un număr mare de utilizatori,​ este foarte posibil să apară și probleme care nu au fost întâmpinate în procesul de dezvoltare și testare. De asemenea, dacă singura informație disponibilă este “Ceva nu a mers cum trebuie la un utilizator”,​ este foarte greu să se rezolve un bug deoarece nu există context; nu știm în ce condiții s-a reprodus bugul.
  
-Din acest motiv, în industrie, aplicațiile produc și salvează mesaje de logging. Acestea conțin diverse informații despre starea aplicației,​ (e.g. funcția ''​%%x%%''​ s-a încheiat cu succes, operația ''​%%y%%''​ a eșuat din cauza erorii ''​%%z%%''​ ) care sunt foarte utile pentru a depista și rezolva problemele care au dus la eroare.+Din acest motiv, în industrie, aplicațiile produc și salvează mesaje de logging. Acestea conțin diverse informații despre starea aplicației,​ (e.g. funcția ''​%%x%%''​ s-a încheiat cu succes, operația ''​%%y%%''​ a eșuat din cauza erorii ''​%%z%%''​ ) care sunt foarte utile pentru a depista și rezolva problemele care au dus la eroare.
  
-De asemenea, în practică se folosesc foarte mult toolurile de debugging (e.g. ''​%%gdb%%''​ ), ce permit verificarea anumitor aspecte ale execuției unui program (e.g. pas cu pas ce instrucțiune se execută, ce zone de memorie se accesează, dacă se fac alocări etc).+De asemenea, în practică se folosesc foarte mult toolurile de debugging (e.g. ''​%%gdb%%''​ ), ce permit verificarea anumitor aspecte ale execuției unui program (e.g. pas cu pas ce instrucțiune se execută, ce zone de memorie se accesează, dacă se fac alocări etc).
  
 ===== Logging ===== ===== Logging =====
  
-Primul tool întâlnit de voi este ''​%%printf%%''​. Adăugarea de mesage de logging în programarele voastre vă poate ajuta foarte ​mult în procesul de debugging.+Primul tool întâlnit de voi este ''​%%printf%%''​. Adăugarea de mesage de logging în programarele voastre vă poate ajuta foarte ​multe în procesul de debugging.
  
 În industrie logurile pot ajuta la reproducerea fidelă a contextului în care a apărut problema semnalată. În industrie logurile pot ajuta la reproducerea fidelă a contextului în care a apărut problema semnalată.
Line 75: Line 75:
 ===== GDB ===== ===== GDB =====
  
-Un ''​%%debugger%%''​ este un ''​%%program%%''​ care permite rularea instrucțiune cu instrucțiune a unui ''​%%alt program%%''​ , inspectarea variabilelor și a zonelor de memorie și chiar modificarea acestora în timp real pentru a repara buguri. Acestă abordare se numește ''​%%analiză dinamică%%''​ , deoarece un program o dată pornit își schimbă starea internă (e.g. variabilele se modifică în timp).+Un ''​%%debugger%%''​ este un ''​%%program%%''​ care permite rularea instrucțiune cu instrucțiune a unui ''​%%alt program%%''​ , inspectarea variabilelor și a zonelor de memorie și chiar modificarea acestora în timp real pentru a repara buguri. Acestă abordare se numește ''​%%analiză dinamică%%''​ , deoarece un program o dată pornit își schimbă starea internă (e.g. variabilele se modifică în timp).
  
-Cel mai popular debugger este GDB ([[https://​www.gnu.org/​software/​gdb/​|The GNU Project Debugger]]). Acesta oferă suport pentru programe scrise în diferite limbaje (e.g. C, C++, D, Rust, Assembly, Go).+Cel mai popular debugger este GDB ([[https://​www.gnu.org/​software/​gdb/​|The GNU Project Debugger]]). Acesta oferă suport pentru programe scrise în diferite limbaje (e.g. C, C++, D, Rust, Assembly, Go).
  
 Vom prezenta în continuare folosirea acestui tool atât din linia de comandă, dar și din IDE. Vom prezenta în continuare folosirea acestui tool atât din linia de comandă, dar și din IDE.
Line 354: Line 354:
 2       ​breakpoint ​    keep y   ​0x000055555555519f in sum_even at buggy.c:12 2       ​breakpoint ​    keep y   ​0x000055555555519f in sum_even at buggy.c:12
 </​code>​ </​code>​
-Acum apare și acesta în lista de breakpoint-uri,​ cu numărul 2. Pentru a șterge un breakpoint, folosim comanda ''​%%d <numar breakpoint>​%%''​ . Să ștergem breakpoint-ul cu numărul 2 și să adăugăm din nou un breakpoint la linia 12.+Acum apare și acesta în lista de breakpoint-uri,​ cu numărul 2. Pentru a șterge un breakpoint, folosim comanda ''​%%d <numar breakpoint>​%%''​ . Să ștergem breakpoint-ul cu numărul 2 și să adăugăm din nou un breakpoint la linia 14.
  
 <code bash> <code bash>
Line 497: Line 497:
 <​details>​ <​details>​
 </​HTML>​ </​HTML>​
-<​html><​summary></​html>​ Spoiler <​html></​summary></​html>​ ''​%%5%%''​ este fix valoarea lui ''​%%n%%''​. Deoarece ''​%%n%%''​ și ''​%%cnt%%''​ sunt variabile locale, ele vor fi alocate pe stivă. Ele sunt declarate consecutiv, de aceea vor avea adrese consecutive pe stivă. Atunci când ''​%%cnt%%''​ este incrementat în funcția ''​%%sum_even%%''​ (''​%%cnt%%''​ este pointer aici), va pointa fix către adresa lui ''​%%n%%'' ​din ''​%%main%%''​.+<​html><​summary></​html>​ Spoiler <​html></​summary></​html>​ ''​%%5%%''​ este fix valoarea lui ''​%%n%%''​. Deoarece ''​%%n%%''​ și ''​%%cnt%%''​ sunt variabile locale, ele vor fi alocate pe stivă. Ele sunt declarate consecutiv, de aceea vor avea adrese consecutive pe stivă. Atunci când ''​%%cnt%%''​ este incrementat în funcția ''​%%sum_even%%''​ (''​%%cnt%%''​ este pointer aici''​%%), va pointa fix către adresa lui%%''​n''​%%din%%''​main`.
  
 Putem verifica acest lucru folosind gdb. Putem verifica acest lucru folosind gdb.
Line 551: Line 551:
 Putem rula un program în modul debugging folosind ''​%%Run -> Start Debugging%%''​ sau ''​%%F5%%''​. Putem rula un program în modul debugging folosind ''​%%Run -> Start Debugging%%''​ sau ''​%%F5%%''​.
  
-După pornirea acestui mod, observăm că prin selectarea meniului ''​%%Run and Debug%%''​ din partea stângă (sau ''​%%CTRL + SHIFT + D%%''​) avem mai multe ferestre afișate:+După pornirea acestui mod, obersăm că prin selectarea meniului ''​%%Run and Debug%%''​ din partea stângă (sau ''​%%CTRL + SHIFT + D%%''​) avem mai multe ferestre afișate:
  
   * ''​%%VARIABLES%%'':​ fereastră unde putem observa valoarea tuturor variabilelor vizibile/​folosite de program într-un moment dat al execuției.   * ''​%%VARIABLES%%'':​ fereastră unde putem observa valoarea tuturor variabilelor vizibile/​folosite de program într-un moment dat al execuției.
-  * ''​%%WATCH%%'':​ fereastră unde putem vizualiza cum o expresie se modifică (e.g. ''​%%cnt%%'',​ ''​%%2 * cnt%%''​). +  * ''​%%WATCH%%'':​ fereastră unde putem vizualiza cum o expresie se modifică (e.g. ''​%%cnt%%'',​ ''​%%2 * cnt%%''​). 
-  * ''​%%CALLSTACK%%'': ​fereastră unde putem vedea succesiunea de apeluri de funcții (e.g. ''​%%g()%%''​ a fost apelată din ''​%%f()%%''​ , care a fost apelată din ''​%%main()%%''​).+  * ''​%%CALLSTACK%%'': ​ferastră unde putem vedea succesiunea de apeluri de funcții (e.g. ''​%%g()%%''​ a fost apelată din ''​%%f()%%''​ , care a fost apelată din ''​%%main()%%''​).
  
 {{https://​ocw.cs.pub.ro/​courses/​_media/​programare/​tutoriale/​debugging/​ide/​ide-01.gif?​900| IDE - 01}} {{https://​ocw.cs.pub.ro/​courses/​_media/​programare/​tutoriale/​debugging/​ide/​ide-01.gif?​900| IDE - 01}}
Line 585: Line 585:
 Un aspect important din cadrul dezvoltării aplicațiilor software îl reprezintă ''​%%managementul memoriei%%'',​ întrucât lucrul incorect cu aceasta (bugurile de memorie) poate duce la numeroase probleme: memory leaks, open files, invalid memory accesses. Un aspect important din cadrul dezvoltării aplicațiilor software îl reprezintă ''​%%managementul memoriei%%'',​ întrucât lucrul incorect cu aceasta (bugurile de memorie) poate duce la numeroase probleme: memory leaks, open files, invalid memory accesses.
  
-— [[https://​en.wikipedia.org/​wiki/​Memory_leak|Memory leaks / memleaks]] (leakuri de memorie) sunt acele ''​%%zone de memorie alocate%%''​ (e.g. cu ''​%%mallloc%%''​) și pentru care aveam ''​%%inițial un pointer către adresa de început%%''​ (e.g. valoarea returnată de ''​%%malloc%%''​ a fost validată și stocată într-o variabilă de tip pointer), dar pentru care ''​%%ulterior am pierdut toate referințele%%''​ (e.g. nu mai avem acei pointeri - poate erau variabile locale într-o funcție și nu au fost salvați în main) și pe care ''​%%nu le mai putem elibera%%''​ (e.g. nu avem ce să pasăm către free).+— [[https://​en.wikipedia.org/​wiki/​Memory_leak|Memory leaks / memleaks]] (leakuri de memorie) sunt acele ''​%%zone de memorie alocate%%''​ (e.g. cu ''​%%mallloc%%''​) și pentru care aveam ''​%%inițial un pointer către adresa de început%%''​ (e.g. valoarea returnată de ''​%%malloc%%''​ a fost validată și stocată într-o variabilă de tip pointer), dar pentru care ''​%%ulterior am pierdut toate referințele%%''​ (e.g. nu mai avem acei pointeri - poate erau variabile locale într-o funcție și nu au fost salvați în main) și pe care ''​%%nu le mai putem elibera%%''​ (e.g. nu avem ce să pasăm către free).
  
 Aceste blocuri de memorie rămân blocate / neutilizabile până când programul se oprește, moment în care sistemul de operare eliberează toate resursele alocate pentru acel program. Aceste blocuri de memorie rămân blocate / neutilizabile până când programul se oprește, moment în care sistemul de operare eliberează toate resursele alocate pentru acel program.
  
-De ce credeți totuși că este un bug grav să ai memory leaks într-un program chiar dacă face sistemul de operare curat la final?+De ce credeți totuși că este un bug grav să ai memory leaks într-un program chiar dacă facem sistemul de operare curat la final?
  
 <​HTML>​ <​HTML>​
Line 606: Line 606:
 Analog putem detecta situații în care alte resurse nu sunt eliberate (în principiu au legătură directă sau indirectă tot cu memoria): Analog putem detecta situații în care alte resurse nu sunt eliberate (în principiu au legătură directă sau indirectă tot cu memoria):
  
-  * ''​%%open files%%'':​ pentru orice fișier deschis (e.g. cu ''​%%fopen%%''​) biblioteca C alocă resurse în spate (e.g. o structură de tipul ''​%%FILE%%'',​ care probabil este alocată cu ''​%%malloc%%''​) trebuie să fie și închis atunci când nu îl mai folosim (e.g. cu ''​%%fclose%%''​);​ operația de închidere dealocă resurse create la deschiderea fișierului (e.g. folosind ''​%%free%%''​);​ prin urmare observăm corespondența directă ''​%%(fopen,​ fclose)%%''​ <-> ''​%%(malloc,​ free)%%''​.+  * ''​%%open files%%'':​ pentru orice fișier deschis (e.g. cu ''​%%fopen%%''​) biblioteca C alocă resurse în spate (e.g. o structură de tipul ''​%%FILE%%'',​ care probabil este alocată cu ''​%%malloc%%''​) trebuie să fie și închis atunci când nu îl mai folosim (e.g. cu ''​%%fclose%%''​);​ operația de închidere dealocă resurse create la deschiderea fișierului (e.g. folosind ''​%%free%%''​);​ prin urmare observăm corespondența directă ''​%%(fopen,​ fclose)%%''​ <-> ''​%%(malloc,​ free)%%''​.
  
 Un tool foarte folosit pentru identificarea bugurilor legate de lucrul cu memoria este [valgrind''​%%](https://​valgrind.org/​) (valgrind este de fapt o colecție de tooluri, însă în acest tutorial vom vorbi despre toolul default,​%%''​memcheck`). Cu ajutorul său putem identifica probleme precum accesarea unei zone de memorie nealocată sau eliberată de pe heap, leak-uri de memorie, fișiere neînchise etc. Un tool foarte folosit pentru identificarea bugurilor legate de lucrul cu memoria este [valgrind''​%%](https://​valgrind.org/​) (valgrind este de fapt o colecție de tooluri, însă în acest tutorial vom vorbi despre toolul default,​%%''​memcheck`). Cu ajutorul său putem identifica probleme precum accesarea unei zone de memorie nealocată sau eliberată de pe heap, leak-uri de memorie, fișiere neînchise etc.
Line 667: Line 667:
 <​html><​summary></​html>​ Spoiler <​html></​summary></​html>​ Aceasta funcție închide imediat procesul care a apelat-o. Toate fișierele deschise de catre proces sunt închise și memoria alocată este eliberată. Această funcție se folosește în general atunci se produce o eroare în urma căreia nu mai vrem să continuăm execuția programului. <​html><​summary></​html>​ Spoiler <​html></​summary></​html>​ Aceasta funcție închide imediat procesul care a apelat-o. Toate fișierele deschise de catre proces sunt închise și memoria alocată este eliberată. Această funcție se folosește în general atunci se produce o eroare în urma căreia nu mai vrem să continuăm execuția programului.
  
-Alternativ, funcția ​''​%%get_perfect_squares%%'' ​ar putea întoarce ''​%%NULL%%'',​ dar nu înainte de a elibera memoria alocată până în acel punct.+Alternativ, funcția ar putea întoarce ''​%%NULL%%'',​ dar nu înainte de a elibera memoria alocată până în acel punct.
  
 <​HTML>​ <​HTML>​
Line 751: Line 751:
 ''​%%Invalid write of size 4%%''​ reprezintă faptul că încercăm să scriem 4 bytes (un ''​%%int%%''​ ). Mesajul ''​%%Address 0x4a55484 is 0 bytes after a block of size 4 alloc'​d%%''​ ne spune faptul că la adresa unde încercăm să scriem avem 0 bytes alocați și că noi am alocat 4 bytes. ''​%%Invalid write of size 4%%''​ reprezintă faptul că încercăm să scriem 4 bytes (un ''​%%int%%''​ ). Mesajul ''​%%Address 0x4a55484 is 0 bytes after a block of size 4 alloc'​d%%''​ ne spune faptul că la adresa unde încercăm să scriem avem 0 bytes alocați și că noi am alocat 4 bytes.
  
-Rulați programul și pentru alte valori ale lui n (e.g. 3, 8, 100). Pentru valori mai mare ale lui ''​%%n%%''​ primim ''​%%Aborted (core dumped)%%''​.+Rulați programul și pentru alte valori ale lui n (e.g. 3, 8, 100). Pentru valori mai mare ale lui ''​%%n%%''​ primim ''​%%Aborted (core dumped)%%''​.
  
 Care este problema? Care este problema?
Line 757: Line 757:
 <​details>​ <​details>​
 </​HTML>​ </​HTML>​
-<​html><​summary></​html>​ Spoiler <​html></​summary></​html>​ Am alocat doar 4 octeți. Din pagina de [[https://​man7.org/​linux/​man-pages/​man3/​free.3.html|man malloc]] putem vedea că parametrul ''​%%size%%''​ pe care îl primește este numărul de octeți. Astfel, alocarea ar trebui să fie ''​%%int* perfect_squares = (int*) malloc(n * sizeof(int))%%''​. Alternativ, putem folosi sizeof(*perfect_squares) în loc de sizeof(int). În caz că vrem sa schimbăm din int în altceva (e.g. long long), nu trebuie să schimbăm în două locuri.+<​html><​summary></​html>​ Spoiler <​html></​summary></​html>​ Am alocat doar 4 octeți. Din pagina de [[https://​man7.org/​linux/​man-pages/​man3/​free.3.html|man malloc]] putem vedea că parametrul ''​%%size%%''​ pe care îl primește este numărul de octeți. Astfel, alocarea ar trebui să fie ''​%%int* perfect_squares = (int*) malloc(n * sizeof(int))%%''​. Alternativ, putem folosi sizeof(*perfect_squares) în loc de sizeof(int). În caz că vrem sa schimbăm din int în altceva (e.g. long long), nu trebuie să schimbăm în două locuri.
 <​HTML>​ <​HTML>​
 </​details>​ </​details>​
Line 857: Line 857:
 <​html><​br/></​html>​ <​html><​br/></​html>​
  
-==== Exemplu ​- get_multiplication_table ====+==== Exempu ​- get_multiplication_table ====
  
 Să mai considerăm încă un exemplu. Avem o funcție care alocă dinamic o matrice în care stocăm tabla înmulțirii. Spre deosebire de exemplul precedent, în acest cod, se apelează ''​%%free(multiplication_table)%%''​ Să mai considerăm încă un exemplu. Avem o funcție care alocă dinamic o matrice în care stocăm tabla înmulțirii. Spre deosebire de exemplul precedent, în acest cod, se apelează ''​%%free(multiplication_table)%%''​
Line 986: Line 986:
             free(multiplication_table);​             free(multiplication_table);​
             perror("​malloc"​);​             perror("​malloc"​);​
-            ​return NULL;+            ​exit(EXIT_FAILURE);
         }         }
     }     }
Line 1023: Line 1023:
 Am prezentat noțiunile de bază despre cum putem să folosim ''​%%valgrind%%''​ pentru a găsi bugurile din cod în mod eficient, aplicate pe câteva exemple. Am prezentat noțiunile de bază despre cum putem să folosim ''​%%valgrind%%''​ pentru a găsi bugurile din cod în mod eficient, aplicate pe câteva exemple.
  
-De obicei rulăm valgrind cu toate flagurile pe care le considerăm utile, pentru a găsi toate problemele deodată: ''​%%valgrind ​ --leak-check=full --show-leak-kinds=all --show-reachable=no --error-exitcode=1 ./​executabil%%''​ (''​%%--show-reachable=no%%''​ surprimă posibile warninguri provocate de biblioteci din sistem; în caz că programul are leakuri, procesul părinte - valgrind - iese cu codul specificat). +De obicei rulăm valgrind cu toate flagurile pe care le considerăm utile, pentru a găsi toate problemele deodată: ''​%%valgrind ​ --leak-check=full --show-leak-kinds=all --show-reachable=no --exit-code=1 ./​executabil%%''​ (''​%%--show-reachable=no%%''​ surprimă posibile warninguri provocate de biblioteci din sistem; în caz că programul are leakuri, procesul părinte - valgrind - iese cu codul specificat).
programare/tutoriale/debugging.txt · Last modified: 2022/01/21 02:03 by radu.nichita
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0