Visual Studio Debugging

Første gang jeg opplevde debugging var på utviklingsverktøyet til Super Nintendo. Vi måtte ha en egen versjon av hardwaren som kaltes en ICE som tillot oss å debugge vår egen kode med en debugger. Jeg husker jeg ble helt “blown away” av tanken og mulighetene dette ga for å finne feil på en effektiv måte. Dette gjorde at vi ikke bare måtte belage oss på logging, men kunne faktisk komme oss gjennom koden vår med god hjelp.

Fast forward noen og 20 år og debuggeren er blitt langt mer enn en kuriositet - det er nærmest en selvfølge. Mitt første møte med Microsofts utviklingsverktøy var allerede på tidlig 90 tallet i DOS. Da var hovedfokuset en god tekst editor og lite annet. I 1993 kom Microsoft Visual C++ og startskuddet for mye av det som har blitt bygget av moderne utviklingsverktøy fra Microsoft. Når jeg plukket opp Visual Studio.NET beta utgaven i 2001, så begynte mye av den samme “blown away” følelsen å komme tilbake. Ikke bare debuggeren, men total pakken som verktøyet representerte. Denne artikkelen fokuserer kun på debuggeren.

Det hele starter i Debug menyen (eventuelt et tastetrykk unna)

2016-01-20_20-55-36

Det å debugge betyr jo i praksis at man skal finne en bug som må fjernes, men vi bruker det også i dag ofte for å bekrefte programflyten. Dette gjøres ved å legge inn avbrekk (breakpoints) i koden vår som vi ønsker at debuggeren skal stoppe på.

Ved å trykke i margen på kodefilen man har oppe, så får man satt inn et breakpoint. Standard tastatursnarvei er F9. Trykker du en gang til med musa eller F9, så fjerner man breakpointet.

2016-01-20_21-08-58

Kjører man opp løsningen og bruker løsningen man har lagd slik at koden man har valgt å pause i blir brukt, så vil den stoppe her:

2016-01-20_21-10-57

Tar man musa over selve breakpointet, får man opp et lite smart verktøy som gjør at vi kan enkelt midlertidig slå av breakpointet uten å fjerne det helt.

2016-01-20_21-11-32

Ikonet til venstre gir deg innstillinger:

2016-01-20_21-13-12

Her kan man f.eks. gjøre handlinger som å logge en melding til output vinduet og man kan også si at den skal bare fortsette å eksekvere.

2016-01-20_21-13-57

I meldingen kan man da legge til variabler og bruke en del innebygde globale variabler som kan være nyttige å få med:

2016-01-21_08-37-36

Dette vil da komme i debug output vinduet:

2016-01-21_08-39-01

En annen nyttig ting er å få debuggeren til å stoppe kun dersom man treffer en bestemt tilstand, slik at man f.eks. slipper å sitte å steppe gjennom en loop eller lignende for å komme til akkurat det tilfelle man ønsker å finne ut om. Ved å legge til conditions kan man da sette opp uttrykk som man ønsker å stoppe på.

2016-01-21_08-16-38

I debuggeren har man også en hel del diagnose informasjon som gjør at man slipper å legge inn manuell timing:

2016-01-21_08-24-20

Trykker man på denne får man opp et diagnose verktøy som hjelper deg å finne flaskehalser i systemet:

2016-01-21_08-25-43

Jeg skal ikke gå inn i hva dette verktøyet kan gi deg, men er absolutt verdt et dypdykk i seg selg.

Ved å ha musen over en variabel man ser så får man opp innholdet av denne:

2016-01-21_08-18-55

Denne kan festes fast ved å trykke på ikonet til høyre. Når den er festet kan man flytte den rundt som man vil og man kan legge til en kommentar for å gjenkjenne den mens man debugger lettere:

2016-01-21_08-22-47

 

Debug vinduer

Det er en hel del vinduer med ulike verktøy som kan være nyttige under debugging. I Debug menyen finner man Windows.

2016-01-21_08-26-47

Det er veldig mange vinduer her, men de man forholder seg mest til er Output, Call Stack, Watch, Autos, Locals og Immediate vinduene. I denne posten vil jeg ikke gå inn på de andre tingene, men nevner at jobber man med kode som bruker flere tråder så er Threads vinduet samt Parallel stacks og watches. Les mer om disse her. For de som jobber med grafikk programmering er det egne debugger ting laget for dette.

Output

Selve output vinduet, nevnt litt tidligere, er hvor debug log meldinger kommer ut. I dette kommer det en del meldinger som også runtimen gir, som f.eks. assemblies som lastes, thread informasjon osv.

Call Stack

For å finne ut hvilken metode som kaller hvilken (function i JavaScript f.eks.) har man Call Stack vinduet. Ikke bare kan man se hvem som kaller hvem, men ved å dobbelklikke på et hvilket som helst nivå i denne stacken så navigerer man seg tilbake og kan se på variabler på det tidspunktet.

2016-01-21_09-15-13

Watch

Som ved å kunne feste variabler fritt rundt, har man også et eget vindu for å holde variabler man ønsker å se på. Man kan ha flere watch vinduer. I et watch vindu kan man også være litt mer avansert, man legge inn uttrykk og man kalle metoder:

2016-01-21_09-22-13

Locals

Det finnes et automatisk watch vindu for variabler som er tilgjengelig i det scopet man befinner seg i. Dette heter locals og blir automatisk populert. Det er et watch vindu og gir deg samme visningsmulighetene.

2016-01-21_09-23-16

Autos

For et snevrere utvalg av variabler, har man autos. Dette gir deg variablene som er involvert i denne og forrige statement. Følger samme formatet som locals og watch vinduene.

2016-01-21_09-23-27

Immediate

Hvis man ønsker å teste ut ulike ting, endre variabler under kjøring og mer, så har man immediate vinduet. Dette er som en REPL. Kan være veldig nyttig for å kunne gjøre adhoc ting under kjøring. Du kan f.eks. påvirke eksekveringen for å endre retning i koden din. Nytt i Visual Studio 2015 er at man også man benytte lambda uttrykk. Dette er også mulig å benytte i watch vinduene.

2016-01-21_09-43-34

Breakpoints

Verdt å nevne er at man kan benytte breakpoints vinduet for å lett se hva slags breakpoints man har og kan også navigere til dem ved å dobbeltklikke dem:

2016-01-21_09-45-48

Exception Settings

Man kan også få debuggeren til å automatisk stoppe dersom en exception skjer, og man kan filtrere ned til hva slags exceptions man er interessert i, dersom man har en del exceptions som man ikke ønsker å se på. I exception settings vinduet kan man sette dette opp:

2016-01-21_09-46-46

Intellitrace

For de som har Visual Studio 2015 Enterprise edition, har man en funksjon som heter IntelliTrace. Dette tillater historisk debugging. Man kan sette et system i produksjon og debugge noe som en bruker har foretatt seg i etterkant. Dette er noe som kan slåes på i produksjon hvis man ikke klarer å reprodusere det lokalt. Jeg har reddet meg selv et par ganger ved å ha dette slått på ved feil som ikke har vært mulig å reprodusere. Viktig å merke seg at dette er ikke noe man ønsker å ha slått på hele tiden. Les mer om IntelliTrace her.

Debug Visualizer

Ønsker man å visualisere kjente typer i løsningen sin på en bedre måte mens man debugger. Så kan man lage sin egen custom visualizer. I dag har man f.eks. visualiseringer for HTML, JSON og XML. Les mer her om hvordan lage dette.

Hva snakket jeg ikke om...

Debuggeren har blitt så kraftig og inneholder i dag debugging for veldig mye mer enn bare vår vanlige “vanilla” C#/C++/VB.NET. F.eks. har muligheten til å se hva som skjer i HTML DOMen hvis man har breakpoints i JavaScript og har satt opp broen til nettleseren. I en XAML basert løsning kan man gjøre tilsvarende for det visuelle treet. Det er garantert en hel del andre ting jeg ikke har snakket om. Debuggeren er kraftig og vokser i omfang for hver release.

Visual Studio Code

Det siste tilskuddet på stammen i utviklerverktøy er Visual Studio Code. Dette er vårt lettvekts, kode sentriske IDE. Visual Studio Code er open source og finnes på GitHub her. Mange av de paradigmene som er etablert i storebror Visual Studio, finnes også i code når det gjelder debuggeren. I tillegg finnes det et community rundt som bidrar til å gjøre VSCode enda bedre. Noe som kan være av interesse er debugger extensionen som er laget for å debugge WebApps via Chrome og bruke debuggeren i VScode, den finnes her.

Helseadvarsel

Selv om debuggeren er der og det er lett å falle inn i en hverdag hvor man stepper gjennom absolutt alt man gjør, så vil jeg påpeke at dette ikke nødvendigvis er et sunnhetstegn. Det er ingen erstatning fra å gjøre ting på en ordentlig måte. Eksempelvis; automatiserte tester vil gjøre at debuggeren ikke er noe man er avhengig av, men benytter når man faktisk står fast - som i og for seg er nettopp i ordets rette forstand det en debugger skal gjøre. Et annet poeng er at lever man livet i debuggeren, så bruker man mest sannsynlig lenger tid enn nødvendig på å utvikle noe. For undertegnede så kjører jeg aldri med debuggeren - med mindre jeg faktisk må debugge noe. Det betyr at jeg f.eks. heller ikke bruker F5 (Run with debugger), men heller Ctrl + F5 (Run without debugger). Med tanke på DotNetCore og ASP.NET 5, så passer dette også veldig bra med modellen hvor kompilatoren fortløpende kompilerer etterhvert som jeg lagrer filene mine. Så konklusjonen; “with great power, comes great responsibility”.