ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 15
Abonament PDF

Debugging în producție

Radu Vunvulea
Solution Architect
@iQuest



DIVERSE

De câte ori nu vi s-a întâmplat să aveți un o problemă în producție sau în mediul de testare pe care să nu o puteți reproduce pe mașina de dezvoltare? Când acest lucru se întâmplă, lucrurile ajung să o ia razna, iar noi încercăm diferite modalități de remote debug. Fără să știm, aceste tool-uri pot să fie chiar lângă noi, însă noi le ignorăm sau nu știm cum să le folosim.

În cadrul acestui articolul voi prezinta diferite modalități prin care putem să facem debug fără să fie nevoie să folosim Visual Studio.

De ce fără Visual Studio?

Deși Visual Studio este un produs extrem de bun, care ne ajută când avem nevoie să descoperim bug-uri și să facem debug, acesta nu o să ne fie de foarte mare folos în producție. În momentul în care avem un bug în producție, regulile jocului se schimbă. În producție aplicația este compilată pentru release, iar debug-ul nu mai este posibil.

Când este nevoie de aceste tool-uri ?

În momentul în care nu putem să reproducem problema pe mașinile de dezvoltare. Orice am face nu putem să reproducem problema pe care o avem. Neputând reproduce problema este ca și cum am căuta acul în carul cu fân.

Dacă întâmplător apare problema, dar fără să avem un scenariu de reproducere, ajungem în același caz amintit mai sus.

Un alt caz ar fi atunci când memoria ocupată de aplicația noastră crește în timp, fenomenul manifestând-se doar la mașinile de producție. Putem doar să bănuim problema, dar nu știm cauza exactă. Din această cauză, putem să ajungem să "reparăm" cu totul alte zone din cod.

Ce soluții avem?

În general avem la îndemână două variante. Prima variantă se bazează în totalitate pe log-uri. Prin intermediul log-urilor putem să identificăm zonele din aplicație care nu funcționează corespunzător. Dar folosirea log-urilor poate să fie cu două tăișuri. Este nevoie să știi ce este necasar să apară în log-uri și cât de des. În caz contrar te poți trezi cu mii de pagini de log-uri nefolositoare și aproape imposibil de analizat. Dacă ajungem să avem prea multe log-uri putem să fim surprinși de schimbarea comportamentului aplicației.

În cazul în care este posibil putem să trimitem PDB-urile pe mașina de producție. Prin acest mod vom avea acces la tot stack trace-ul pe care o excepție îl generează.

Log-urile ne pot fi de mare ajutor pentru a rezolva diferite probleme care apar în producție. Chiar dacă log-urile sunt foarte folositoare, nu ne vor ajuta de fiecare dată. Există diferite probleme care pot să apară și care să fie extrem de greu de identificat folosind log-urile. De exemplu un dead-lock ar fi aproape imposibil de identificat prin intermediul log-urilor.

O altă variantă pe care o avem la dispoziție este crearea de memory dump-uri și analizarea acestora.

Ce este un memory dump

Un memory dump este un snapshot a procesului într-un anumit moment. Pe lângă informațiile despre alocarea memoriei, un snapshot conține informații despre starea diferitelor thread-uri, obiecte și cod. Folosind această informație putem să obținem informații foarte valoroase despre procesul care rulează. Acest snapshot reprezintă imaginea memoriei în format 32 sau 64 de biti, în funcție de sistem.

În general există două tipuri de memory dump. Primul este minidump. Acesta este cel mai simplu memory dump care se poate face și conține doar informații despre stack - starea procesului sau despre ce apeluri se fac și așa mai departe.

Al doilea tip de memory dump este full dump. Acesta conține toate informațiile care se pot obține, incluzând un snapshot la memorie. Timpul necesar pentru obținerea unui full dump este mult mai mare în comparație cu un minidump, iar fișierul de dump în sine este mult mai mare.

Cum putem genera un memory dump?

Există diferite aplicații care ne ajută să facem acest lucru. Unele din acestea ne permit să facem un dump în mod automat în funcție de diferiți parametri.

În cazul în care este nevoie să facem un memory dump cea mai simplă soluție este din Task Manager. Tot ce este nevoie să facem este să dăm click dreapta pe un proces și să selectăm "Create dump file". Același lucru îl putem face folosind Visual Studio sau "adplus.exe". Ultima variantă este un tool de debug pentru Windows care se regăsește pe aproape toate mașinile care rulează Windows.

În următorul exemplu specificăm la adplus să ne creeze un memory dump în acest moment:

adplus -hang -o C:myDump -pn MyApp.exe

Prin intermediul opțiunii pn specificăm numele procesului pentru care dorim să creăm un dump. În cazul în care dorim să creăm un dump în mod automat putem să folosim opțiunea -crash.

adplus -crash -o C:myDump -pn MyApp.exe
adplus -crash -o C:myDump -sc MyApp.exe

Dacă este nevoie să creăm un dump în mod automat pe lângă "adplus.exe" putem să folosim DebugDiag și "clrdmp.dll". Cele trei opțiuni pe care le avem pentru crearea unui dump în mod automat sunt destul de similare. DebugDump ne permite să configurăm sistemul ca să creeze automat un memory dump în momentul în care nivelul CPU-ului este mai mare de X% într-un anumit interval de timp.

Pe lângă aceste tool-uri există multe altele pe piață. În funcție de necesități puteți să folosiți orice tool de acest tip.

Cum analizăm un dump?

Debugger-ul nativ pentru un dump este reprezentat de Windbg. Acesta este un tool de puternic, cu care se pot obține informații foarte prețioase. Singura problemă a acestui tool este că nu este prietenos. Vom vedea puțin mai târziu care sunt alternativele la Windbg. Trebuie să ținem cont că în aproape toate cazurile alternativele la Windbg folosesc în spate acest debugger - doar că expun o interfață mai prietenoasă și mai utilă.

O alternativă la Windbg este orice Visual Studio mai recent decât Visual Studio 2010. Începând cu Visual Studio 2010, acesta ne oferă posibilitatea să analizăm dump-urile pentru .NET 4.0+. Ceea ce putem să facem în Visual Studio nu este la fel de avansat în comparație cu ceea ce ne permite Windbg, dar în general poate să ne fie de ajuns.

Windbg

Primul pas pe care trebuie să îl facem după ce deschidem Windbg este să încărcăm un dump (CTRL+D). Odată încărcat, un dump poate să fie vizualizat din diferite moduri. De exemplu putem să analizăm thread-urile, memoria, resursele alocate și așa mai departe.

Pentru a putea face mai mult, de exemplu să vizualizăm și să analizăm codul managed avem nevoie să încărcăm librării ajutătoare precum Son of Strike (SOS) sau Son of Strike Extension (SOSEX). Aceste două librări ne deschid noi uși, putând să analizeze datele din dump într-un mod extrem de folositor.

Son of Strike (SOS)

SOS ne permite să vizualizăm procesul în sine. Ne permite să accesăm obiectele thread-urile și informațiile din garbage colector. Putem să vizualizăm inclusiv nume de variabile și valoarea acestora.

Trebuie știut că toate informațiile care se pot accesa fac parte din managed memory. Din această cauză, SOS este strâns legat de CLR și de versiunea acestuia. În momentul în care încărcăm modulul SOS, trebuie să avem grijă să îl încărcăm pe cel corespunzător pentru versiunea de .NET a aplicației noastre.

.loadby sos mscorks
.loadby sos clr

În exemplele de mai sus am încărcat modulul de SOS pentru .NET 3.5-, iar în al doilea exemplu am încărcat SOS pentru .NET 4.0+.

Toate comenzile SOS încep cu "!". Comanda de bază este "!help". În cazul în care dorim să vizualizăm lista de thread-uri putem să ne folosim de comanda "!threads" care are un output asemănător cu următorul:

0:000> !threads
ThreadCount: 5
UnstartedThread: 0
BackgroundThread: 2
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
     ID    OSID    ThreadOBJ Count Apt Exception
…

Debug la un crash

Pănă acuma am văzut că avem la dispoziție multe tool-uri pentru a crea și a analiza un dump. Acuma a venit momentul să vedem ce trebuie să facem pentru a putea analiza un crash.

1. Lansăm procesul

2. Înainte "să crape", comandăm adplus-ului să creeze un dump în momentul în care procesul "crapă"

adplus -crash -pn [numeProcesor]

3. Lansăm Windbg (dupa crash),

3.1. Încărcăm dump-ul,

3.2. Încărcăm SOS,

3.3. !threads (pentru a vedea ce thread a crăpat),

3.4. !PrintException (pe thread-ul care a crăpat pentru a vedea excepția),

3.5. !clrstack (pentru a vedea stack-ul de apeluri),

3.6. !clrstack -a (pentru a vedea stack-ul împreună cu parametri),

3.7. !DumpHeap -type Expcetion (listeză toate excepțiile care nu sunt legate de GC).

Trebuie știut că rezultatele sunt în funcție de modul în care aplicația este compilată. De exemplu, dacă s-a făcut optimizare de cod în momentul compilării. Totodată lista de excepții pe care o putem obține poate să fie destul de lungă din cauza unor comenzi precum !DumpHeap ne returnează toate excepțiile produse - chiar și cele pre-create precum ThreadAbord.

Cum identificăm un deadlock?

Un deadlock apare în momentul în care două sau mai multe thread-uri așteaptă dupa aceeași resursă. În aceste cazuri o parte din aplicație dacă nu chiar toată aplicația se blochează.

Pentru acest caz primul pas este să creăm un dump folosind comanda:

Addplus -hang -o -c:myDump -pn [NumeProces]

Apoi va fi nevoie să analizăm stack trace-ul pentru fiecare thread și să vedem dacă este blocat (Monitor.Enter, ReadWriteLock.Enter…). Odată ce am identificat aceste thread-uri putem să găsim resursele folosite de fiecare thread, împreună cu thread-ul care ține blocate aceste resurse.

Pentru acești ultimi pași comanda "!syncblk" ne vine în ajutor. Aceasta ne listează blocurile de memorie pentru un anumit thread.

Sumar

În cadrul acestui articol am descoperit cum putem să creăm un dump și care sunt tool-urile de bază pentru a-l analiza. Prin intermediul fișierelor dump putem să accesăm informația pe care nu am putea-o accesa în mod normal. Unele date pot să fie accesate doar prin aceste dump-uri și nu prin alte moduri (debug din Visual Studio).

Am putea afirma că aceste tool-uri sunt puternice, dar sunt destul de greu de folosit, necesitând o curbă de învățare destul de mare.

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects