Debugging af software - en fin kunstart, men hvor lærer man det?

Jeg har mega travlt på arbejdet, og en del gange per uge er det debugging-arbejde som dukker op. Det har slået mig at nogle er dygtige til dette og andre er “mindre gode”. Jeg vil gerne have en diskussion med jer om dette nedenfor.

Under uddannelsen på DTU har jeg lært “udvikling” og “metoder”, men ikke ret meget “fejlsøgning” og “debugging”. Jeg debugger selv meget, og er hård til det på en Linux maskine. Hvordan lærer man det? Er det selvstudie? Det var det for mig.

Det er da lidt sjovt, at mange firmaer nok vil nikke genkendende til at 20% af tiden går med udvikling, mens 80% af tiden går med test og fejlsøgning. Især i store systemer, som dem jeg roder med er det nok ikke helt skævt.

Lige før jul havde jeg dette output fra valgrind (tool til at finde fejl i C/C++ kode)

==24888== More than 10000000 total errors detected. I'm not reporting any more.
==24888== Final error counts will be inaccurate. Go fix your program!

God humor. Det kom Det skyldes at min header-filer var gået i hegnet så alt gik ned.

Der er for mig flere elementer i af lære at debugge godt:

  • Forståelse af opgaven. Hvis man ikke forstå hvad programmet burde gøre, står man dårligt.
  • Forståelse af programmeringsproget. Det er oftest små fejl, der kan få store konsekvenser. For C/C++ er det oftest pointere og array problemer jeg ser.
  • Forståelse fra hjælpetools. Til C/C++ bruger jeg jævnligt valgrind, lint-kode, og purify. Lær at bruge tools, der automatisk kan vise, at der er fejl. Gdb eller lignende på Linux er også et must.
  • Lav automatiske tests - mange og ofte. Folk hader det, men det er eneste måde at leve med store kodemængder. For fejl, man finder og retter, som andre muligvis kan ramme senere - tilføj en test for dette.
  • Med komplekse fejl er det guld at lære at reducere kode hvor man bibeholder fejlen. Med store kodemængder har jeg oftest stor succes med at skære koden ned og se om fejlen stadig er der. Til sidst ser jeg fejlen. Langsomt metode, men god.
  • Design debug-muligheder ind fra starten af designet.
  • Brug versions-kontrol aktivt: Check om fejl kan genskabes på gamle versioner. Hvis fejl er kommet til er versions-kontrolsystemet et kæmpe aktivt til at debugge hvornår fejlen startede.
  • “print”-debugging. Det er meget ofte et godt alternativ til gdb-debugging at skrive centrale variable ud på i en fil eller på skærmen og følge dem.
  • Stædighed og effektivitet. Man kommer ikke uden om at stædighed er et must. Jeg har jævnligt debugget et par dage af gangen og enkelte gange flere uger. Drøn hårdt, og derfor er det en kæmpe hjælp at være effektiv med alle de tools man har til rådighed.

Anne-Sofie har tidligere blogget angående debugging: Debug sammen med en anden person. Læs mere her. Det er klart en god ide.

Det største problem jeg jævnligt har med at debugge, er at metoden til at finde fejlen afhænger meget af kontekst - dvs. jeg skal i situationen designe min måde at angribe fejlen på. Det er nok her at min erfaring spiller ind, men jeg ville gerne tættere på at kunne beskrive dette systematisk. Del meget gerne ud af jeres erfaringer nedenfor!

Hvad gør I?

/pto

UPDATE: Jeg har søndag kl 11 tilføjet et par gode råd fra Anne-Sofie, PHK og Allan. * UPDATE2: Jeg har mandag tilføjet "Brug versions-kontrol aktivt"

Peter Tofts billede
Peter Toft er algoritme-designer hos Fingerprints Cards. Han har blogget om open source og Linux siden Version2's begyndelse. Blogger også jævnligt om andre sjove teknologi-områder.

Kommentarer (42)

Mogens Hansen

Arbejdet med at debugge starter mens softwaren designes og udvikles. Hav et klart design, tænk i invarians, pre- og post-condition. I C og C++ kan det ofte udtrykkes ved assert.
Brug en kodestil der er effektiv og samtidig undgår fejl som er nemme at lave. Det vil i C++ sige undgå normalt brug af pointere og eksplicit "new" på applikations niveau - brug klasser som std::vector, std::string og std::map i stedet.
Brug værktøjer som du nævner - statiske analysatorer (á la Lint) og dynamiske analysatorer (som Valgrind og Purify, men gerne nogle som kender mere til source koden som BoundsChecker og måske Clang AddressSanitizer, MemorySanitizer).
Hold antallet af fejl nede (på 0 kendte), så man ikke pludselig står med 1000000 fejl der skal undersøges, prioriteres og rettes.

Kai Birger Nielsen

Svært enig :-) Læsbare logfiler med relevant og tidsstemplet information er også guld værd. Det er meget bedre at kunne sige: "Gjorde nogen noget ved storage på det her system i fredags kl 15.14?" end "Er der nogen, der har en ide om hvorfor der pludselig er svartidsproblemer på det her site?"(Underforstået: Jeg har ingen ide om hvornår det startede eller hvad normalsituationen plejer at være.)
Og det er meget meget nemmere at debugge kode/systemer, hvor det er tænkt ind på forhånd at noget kan gå galt.

Pelle Söderling

Der er én regel når det kommer til debugging: Never make assumptions.

Når du begynder at gætte på hvad fejlen mon kan være eller lave antagelser såsom "det virkede jo for lidt siden, så det må være noget jeg lige har gjort", "Det kan umuligt være det og det, fejlen må være herovre" osv. - når man er ude i den slags så er man hoppet fluks ned i en af de klassiske pitfalls indenfor debugging og så er det at det ender med at tage 80% af tiden.

Midlet derimod er at stoppe gættelegen og valider hvad der foregår. Der er et par værktøjer der er gode at kunne til visse scenarier, men det meste validering kan løses ved at udskrive variabler eller indsætte print statements der kan vise forløbet igennem koden (mange gange finder jeg faktisk dette hurtigere og nemmere end at steppe igennem med en debugger).

Det er meget sjældent fejl tager mig mere end 5 minutter at spotte idag, men det har taget mig adskillige år at vænne mig af med at lave antagelser når jeg debugger og jeg kan stadig fange mig selv i det indimellem - problemet er at det går lidt imod den menneskelige natur, vi er så vant til problemløsning at vi elsker at gætte på hvad fejlen er.

Men det er aldrig det værd, for man ender med at sidde og bruge 30 minutter på gætteleg (eller nogle gange mere, jeg har set udviklere sidde i timevis på at prøve at debugge trivielle scenarier), istedetfor 5 minutter på at træde et skridt tilbage og begynde at validere hvad der rent faktisk foregår.

Jeg tror desværre ikke der er nogen steder man kan lære om dette, hvilket er ærgeligt for jeg oplever at mange udviklere bruger uforholdsmæssig meget tid på at debugge fremfor at udvikle.

Pelle Söderling

Peter Frandsen: Nu har jeg siddet og kigget nogle af videoerne igennem og jeg synes helt ærligt at han netop underviser i at opstille hypoteser, foretage eksperimenter osv. ergo det er undervisning i debugging på gætværksform (hvilket man self. også kan blive god til, men det bliver aldrig rigtig effektivt).

Det står skidt til hvis selv dem der forsøger at lære fra sig tror debugging handler om at redde prinsessen efter en heroisk indsats hvor de undervejs har slået dragen ihjel på 17 forskellige måder.

Kim Hjortholm

ISTQB certificering er et god bud på at lære den metodemæssige del af test/debugging. Når det er sagt så er test/debugging nemmest hvis det tænkes ind i design og kode fra starten af.

Effektiv Brug af pre- og postcondition er vigtig, ideelt set skal der være mulighed for at slå logning af pre- og postcondition til via debug flags. Brug af testcases der starter smalt og bliver "bredere" (læs: mere komplekse).

Segmenter test af system/modul/kode indtil sidste valide pre- eller postcondition er fundet.

Afhængig af viden om arkitektur/koden kan man så foretage blackbox eller whitebox test til at identificere den del af koden der er fejlbehæftet,

Allan Ebdrup

Jeg er meget enig med det du skriver.
Skridt et, vil altid være at reproducere fejlen (hvis den sker i produktion). Allerede her skilles fårene fra bukkene, har du overhoved nok information til at reproducere problemet?
Her er specielt fejl som memoryleaks eller concurrency problemer slemme. I denne indledende fase er du prisgivet, hvis dit system ikke giver dig nok information, og så bliver første skridt måske at tilføje ekstra logging osv i produktion.
Når man først har reproduceret en fejl konsekvent, er det i min bog oftest ret trivielt at finde ud af hvad der skal rettes. Og der er jeg helt med på "Never make assumptions"-vognen. Jeg vil også sige: pas på med at sidde for længe og rode rundt i en debugger. Oftest er nogle console.logs og nogle asserts meget mere effektivt.

Hvis man ikke passer på kan man sidde og rode rundt i en debugger i timevis.

Peter Toft

Aaah - Anne-Sofie, den kunne godt være med på listen af gode råd.

Jeg har også jævnligt fundet fejl i egen defekt kode ved at forklare hvad jeg laver overfor andre. Midt i min forklaring kan jeg så stoppe med "aaah - nu forstår jeg det".

Lars Tørnes Hansen

Jeg har en b der hedder: "Debug It! Find, Repair & Prevent Bugs in You Code" af Paul Butcher, ISBN 978-1-93435-628-9

http://pragprog.com/book/pbdp/debug-it
De skriver om bogen under "Deails":

Debug It! will equip you with the tools, techniques, and approaches to help you tackle any bug with confidence.
These secrets of professional debugging illuminate every stage of the bug life cycle, from constructing software that makes debugging easy; through bug detection, reproduction, and diagnosis; to rolling out your eventual fix.

Whether you’re writing Java or assembly language, targeting servers or embedded micro- controllers, or using agile or traditional approaches, the same basic bug-fixing principles apply.

You’ll learn an empirical approach that leverages your software’s unique ability to show you what’s really happening, the importance of finding a reliable and convenient means of reproducing a bug, and how to avoid common pitfalls.

You’ll see how to use commonly available tools to automatically detect problems before they’re reported by customers.
You’ll construct “self-debugging” software that automatically provides access to crucial internal information and identifies the broken assumptions that lead to bugs.

Amazon.co.uk side: http://www.amazon.co.uk/Debug-It-Prevent-Pragmatic-Programmers/dp/193435...
og beskrivelsen der:

Some developers thrash around aimlessly looking for a bug without concrete results. Others have the knack of unerringly zeroing in on the root cause of a bug. Are they geniuses? Just lucky? No, they've learned the secrets of professional debugging. This book will equip you with the tools, techniques and approaches-proven in the crucible of professional software development-to ensure that you can tackle any bug with confidence.

You'll learn how to handle every stage of the bug life-cycle, from constructing software that makes debugging easy, through detection, reproduction, diagnosis and rolling out your eventual fix.

If you develop software, sooner or later you're going to discover that it doesn't always behave as you intended. Working out why it's misbehaving can be hard. Sometimes very hard. Debug It! is here to help!

All bugs are different: there is no silver bullet. You've got to rely upon your intellect, intuition, detective skills and yes, even a little luck. But that doesn't mean that you're completely on your own-there is much you can learn from those who have gone before. This book distills decades of hard-won experience gained in the trenches of professional software development, giving you a head-start and arming you with the tools you need to get to the bottom of the problem, whatever you're faced with.

Whether you're writing Java or assembly language, targeting servers or embedded micro-controllers, using agile or traditional approaches, the same basic bug-fixing principles apply. From constructing software that is easy to debug (and incidentally less likely to contain bugs in the first place), through handling bug reports to rolling out your ultimate fix, we'll cover the entire life-cycle of a bug.

You'll learn about the empirical approach, which leverages your software's unique ability to show you what's really happening, the importance of finding a reliable and convenient means of reproducing a bug, and common pitfalls so you can avoid them. You'll see how to use commonly available tools to automatically detect problems before they're reported by customers and how to construct "transparent software" that provides access to critical information and internal state.

Allan Ebdrup

Jeg glemte at nævne, at en fast del af at debugge en fejl for mig, er at lave en automatisk test der er rød, så snart jeg har nok information til det. Og så gøre testen grøn. Så sikrer du dig at fejlen ikke bliver introduceret igen af en anden udvikler.

Det commit jeg laver hvor jeg fikser fejlen skal også indeholde testen.

Mark Ruvald Pedersen

Jeg strør omkring med så præcise asserts over det meste. Hvis en betingelse jeg dømmer vigtig for programmets opførsel skal overholdes, men ikke kan skrives som en boolean expression, så udfældes checket til en funktion -- en inline unit-test om man vil. Det er godt, hvis den kodebase man overtager ikke er modulær nok til at kunne drives fra eksterne unit-tests. Og i produktion compiles asserts selvf bare ud :-)
Pre- og post asserts er ikke nok.

Debugging med en debugger er fint hvis man blot vil inspicere de sidste udgaver af ens variable, eller hvad call-stacken var der ledte op til breakpointet. Men jeg har ofte ønsket at single steppe tilbage i tid. Dertil har jeg ikke fundet en debugger hvor dette er muligt. Kender I nogen? Af den grund bruger jeg også tracing macros.

Søren Rindal Nielsen

"If debugging is an art, we should let Picasso do it" - sådan ca. står der i: http://www.amazon.co.uk/Debugging-Indispensable-Software-Hardware-Proble....

Bogen indeholder 9 vældig gode punkter som tilsammen kan lære læseren håndværket at debugge. Punkterne er:

UNDERSTAND THE SYSTEM  
MAKE IT FAIL  
QUIT THINKING AND LOOK  
DIVIDE AND CONQUER  
CHANGE ONE THING AT A TIME  
KEEP AN AUDIT TRAIL  
CHECK THE PLUG  
GET A FRESH VIEW  
IF YOU DIDNT FIX IT, IT AINT FIXED

Et must-read hvis du vil løfte din debugging fra ad-hoc til solidt håndværk.

Pelle Söderling

Jeg begynder at tro at behovet for at tale om debugging er ret stort, det illustreres nok bedst ved at de fleste i denne tråd taler mest om at undgå debugging.

Men vi ved også allesammen at før eller siden er der brug for at debugge, så istedetfor at gå rundt om den varme grød vil jeg prøve at uddybe min "never make assumption" model lidt mere.

Der er 2 væsentlige former for debugging: Debugging under udvikling og debugging under drift (produktion).
Ved debugging under udvikling har du somregel langt bedre muligheder for at se hvad der sker og genskabe scenarier, end du har når et drift-miljø f.eks. bruger meget CPU/Memory, crasher eller lign.

DEBUGGING UNDER UDVIKLING:

Jeg og en makker begyndte at studere debugging lidt mere indgående for en 6-7 år siden. Det gjorde vi fordi vi oplevede at vi selv og folk omkring os brugte rigtig meget tid på dette - så enhver optimering af denne process ville potentielt være en stor gevinst.

Det vi erfarede undervejs var at når noget tog timer eller endda dage at debugge, så skyldtes det somregel at vi havde gjort os en eller flere antagelser undervejs - ofte rationelle antagelser, men desværre har debugging det ofte med at udfordre sådanne rationelle antagelser.

Derfra udsprang vores mantra om "aldrig at lave antagelser" i forbindelse med debugging.
Men hvordan gør man det i praksis? Det er noget der tager tid at lære, for det ligger dybt i os at foretage sådanne antagelser i et forsøg på at skyde genvej til en given løsning - oftest fører det os desværre bare ad omveje, nogle gange endda store omveje.

Måden man gør det på er ved at smide alt man tror man ved om hvordan ens program/sproget/compileren etc. virker om i baghovedet og begynde at validere hvad der foregår systematisk. Det kan starte med noget så simpelt som at lade være med at antage at en eller anden given metode bliver kaldt, start med at validere at den faktisk bliver kaldt (smid et simpelt printstatement i starten). Begynd derefter systematisk at udskrive data så du kan se hvad der foregår og smid gerne nogle print statements ind i den metode du er igang med at debugge der blot har til formål at tracke forløbet igennem metoden (lad være med at brug en masse tid på at navngive dette output, du er ikke ved at dokumentere koden, så "1","2","3" eller "Metodenavn:1","Metodenavn:2","Metodenavn:3" fungerer fint).
Efter en kørsel eller 2 burde du gerne være blevet klogere, enten har du opdaget problemet eller også er du nu blevet klogere på i hvilken metode det går galt og kan flytte dit debugfokus et skridt længere ind og gentage processen.

Det er selvfølgelig ikke alle scenarier man kan klare med denne metode, men langt det meste trivielle debugging (som faktisk er det de fleste bruger det meste af deres tid på) bliver netop trivielt hvis man fra start går systematisk til værks på denne måde istedetfor at opstille hypoteser om hvad problemet mon kan være og efterprøve dem som om man var videnskabsmand. For mange af de mere komplekse scenarier oplever jeg dog at mantraet stadig gælder, men det kan kræve lidt andre værktøjer og metoder.

Et andet eksempel kunne være debugging af Ajax kald, istedetfor at sidde og gætte sig frem til tingene så benyt værktøjer som Fiddler, Firebug og lign. der nemt kan vise dig requests og data så du kan validere hvad der foregår.

Nogle gange kan det være sværere at validere hvad der foregår, f.eks. hvis man har en stor HTML fil og et eller andet i denne HTML fil forårsager at visningen fucker op på en måde så det er svært at identificere hvilken del af filen der forårsager problemet. Da du ikke kan hooke ind i browserens renderingsprocess, kan det være nødvendigt at gå lidt anderledes tilværks. "Inspect element" i de fleste browsere vil ofte kunne hjælpe til at tracke en sådan fejl, men der kan være tilfælde hvor det giver mening at benytte sig af en slags binær søgning teknik. Den virker ganske enkelt ved at man så vidt muligt (sørger for siden stadig er gyldig self.) forsøger at fjerne halvdelen af koden i filen og tjekker om man stadig har fejlen og fortsætter på samme måde i den del der viser sig at have fejlen. På den måde kan man så grave sig ind til problemet og det går faktisk ofte hurtigere end man tror. Denne teknik kan også anvendes i mange sammenhænge der ikke har med HTML at gøre også, specielt layoutdokumenter, men til klassisk kode virker førnævnte teknik bedre.

PRODUKTIONSDEBUGGING:

De klassiske problemer indenfor produktionsdebugging er ting som højt CPU forbrug, højt memory forbrug, crashes og lign.
Hvis du ikke ved hvordan du skal gribe sådanne problemer an, så kan du sidde herfra og til påske og forsøge at komme op med fixes.

Igen gælder det om at lade være med at gætte og begynde at analysere hvad der foregår inde i den kørende applikation.
På Windows betyder det ofte at man har brug for at benytte et værktøj der er alt for lidt kendt blandt udviklere, dette værktøj kaldes WinDBG og gør det muligt at analysere dumps af kørende processer. Så det handler om at analysere stack traces, hvilke objekter der ligger i hukommelsen (hvad hver type fylder, hvor mange instanser er der osv. - er der noget der virker skævt her?), tjekke locks, exceptions osv.

Der findes lign. værktøjer til Java applikationer, til Linux osv. - grundlæggende handler det om at lære at bruge sådanne værktøjer til at validere hvad der foregår i det kørende program, istedetfor at forsøge at gætte sig frem til fixes.

Da produktionsdebugging generelt er meget tidskrævende, kan det godt give mening at tale lidt om hvordan man kan hjælpe sig selv lidt her med at spotte problemer. Noget vi de senere år har gjort meget ud af er at implementere instrumentering i vores drift-systemer. En typisk ting man ønsker at logge i en serverløsning kunne f.eks. være at logge samtlige metodekald og tiden det tager at afvikle dem. I en serverløsning hvor man kan have mange tusinde samtidige brugere stiller denne form for logning nogle krav for at logningen ikke er det der trækker performance ned, men det er uproblematisk at logge f.eks. 1 entry i minuttet pr. metode og i denne entry logger du så: hvor mange gange blev metoden kaldt i intervallet, hvad var gennemsnitsperformance, hvad var best case performance og hvad var worst case performance. Sørg for at logningen af disse data ikke påvirker den request der er ved at blive udført (asynkron logning).
Denne information kan være guld værd til at spotte driftproblemer, jeg ved ikke hvor mange gange vi har oplevet at gennemsnitsperformance var 10ms for et BL kald, men worst case kunne ske at spike op i 20 sekunder - så er det man ved man har et problem, noget der uden instrumentering kan være svært at spotte fordi når du selv prøver oplever du formentlig kun 10ms cases.
Med instrumentering kan du også logge mere BL orienterede værdier - hele tiden handler det dog om at strike den rette balance mellem detaljeret information og hvad der er muligt at logge uden at logningen er det der pludselig bliver flaskehalsen, men hvis du ikke allerede bruger instrumentering håber jeg om ikke andet at jeg hermed har gjort dig nysgerrig.


Det var lidt af mine erfaringer og teknikker.
Det blev et helt blogindlæg i sig selv, men håber at nogle af jer vil finde det brugbart.

Thomas Løcke

Det er min oplevelse/erfaring at rigtig mange programmører (og PHB'ere) er meget forhippede på at få stablet funktionalitet på benene ASAP. De foretrækker at bygge et stort skrummel af et hus der "kan noget", og så satser de på at rette det ind efterfølgende.

Det er efter min bedste overbevisning en mindre god metode.

Nu har jeg selv en baggrund i byggeriet, og her gælder en fast regel for gode håndværkere: Der kommer ingen god helhed ud af at bygge af dårlige grundkomponenter. Ja, det er hurtigere bare at male på den rustne overflade, men det holder markant længere hvis du først sandblæser og grunder. Tiden brugt på sandblæsning og grunding er vældig godt givet ud.

Software skal bygges af veldefinerede enheder der er af en størrelse vores hjerner kan kapere (læs: meget små). Disse små enheder skal solidt testes efterhånden som de produceres. Disse tests skal gerne bygges af andre programmører end dem der laver selve enheden, eller de skal i det mindste rubberduck debugges med en anden bevidsthed end ens egen. Når enheder "limes" sammen med andre enheder, så skal den nye konstruktion også testes og rubberduck'es godt igennem. Rinse and repeat.

Jeg tror desværre ikke at ret meget andet end erfaring med software der skal vedligeholdes og udvikles over mange år kan bibringe folk den nødvendige forståelse af disse forhold. De fleste af os har en tendens til at overvurdere vores egne evner til at forstå (og huske) intention og funktion i egen kode.

Det er min erfaring at rubberduck debugging med en anden person er enormt værdifuldt. Bare det højt at fortælle om ens kode til en anden person har en tendens til at afsløre meget af det svage.

Pelle Söderling

Jeg er faktisk ikke specielt glad for rubberduck debugging.
Ja der er ofte at det virker, men det er ikke nødvendigvis effektivt og der er mange scenarier hvori den ikke virker fordi fejlen skyldes noget du ikke har kendskab til.

Rubberduck metoden er lidt en "lad fejlen komme til mig" løsning, istedetfor at gå systematisk til værks, udskrive data og aktivt finde problemet.

Såfremt man vælger at involvere en anden person (istedetfor den gule badeand), så bliver det endnu mere ineffektivt fordi nu forstyrrer du også denne person i sit arbejde og bruger også denne persons tid.

Pointen værende at rubberduck debugging reelt kun kan løse "tanketorsk" fejl, altså trivielle debugging scenarier - hertil er en systematisk tilgang til debugging langt mere effektiv og der er slet ikke noget behov her for at involvere andre mennesker i den process, så er det fordi man har en forkert tilgang til debugging.

Allan Ebdrup

Det gør jeg også normalt, hvis jeg vurderer at andre muligvis kan ramme den senere


Fejlen er ramt en gang, det er som regel nok for mig.
Jeg synes nu også der er mere i det end det:
- Testen gør der meget nemmere for ham/hende der skal review'e dit commit
- Testen vil hjælpe ved en refaktorering
- Testen fungerer også langsigtet som dokumentation af systemet

Jeg vil ikke påstå at det er hver gang at jeg retter en fejl, at der følger en test med. Men det er tæt på.

Kim Hjortholm

ISTQB certificering tilbydes af flere i DK.

www.testhuset.dk er en af disse (ingen affilering med testhuset - men var dem jeg blev certifceret via), prøv f.eks. denne søgning for at se udvalg, priser og tider https://www.google.com/search?q=istqb+certificering+danmark

Pensum materiale for ISTQB kan downloades her http://www.dstb.dk/wss/default.asp?page=2780

Henrik B Sørensen

I min verden er defensiv programmering et must! Det samme er, at man skal sgu holde styr på livsforløbet på objekter etc. Ellers får man tæsk af programmet senere :-( En gang imellem glipper det, eller man overtager noget kode fra andre, som ikke funker og hvad så?

De fleste af mine erfaringer er fra Windows, men de BURDE være så generiske, at de kan bruges til Linux også ( jo mere, man "leger" med gdb, jo mere misundelig bliver man - så gode kilder til at lære mere om gdb modtages med kyshånd evt. på PM ) i en mere "løs" form :

  1. "HJÆLP!!! Der er sket en fejl! SKIDTET CRASHER!!!"
  2. "Rolig nu.. Hvad gør programmet? Og ( næsten vigtigere ) hvad BURDE det gøre?"
  3. Hent kaffe.
  4. Kan fejlen reproduceres? Og for forskellige brugere (a)?
  5. Hent kaffe.
  6. Fat i aktuelle version af kildekoden - kan det overhovedet compilere?
  7. Kan vi skaffe log-filer, crash dumps etc.? ( hvis nej, skaf en cockerspaniel, et pølsebrød og en pakke Stryhn's Grovhakket og opsøg udvikleren ).
  8. Hent kaffe.
  9. Er der noget, som giver mening ud fra log / dump?
  10. Hent en kande kaffe.
  11. Find "kardinalpunkter" ( punkter, hvor programmet foretager sig noget 'flow-ændrende' ) og sæt debuggeren på.
  12. Hent kaffemaskinen ind til bordet sammen med en god forsyning af kaffebønner, filtre og vand.
  13. Virker skidtet under debugging? Så KAN det være timing / race conditions. ( band godt og grundigt - og ring efter cockerspanielen ).
  14. Hvis programmet har netadgang - start WireShark etc. op og "snif" løs. (b)
  15. Har programmet USB / serial adgang - start en anden "sniffer" og go nuts.
  16. Kan fejlen isoleres til en enkelt enhed, men kan du ikke se hvor, den er? Refaktorér og Reducér.
  17. Blev fejlen løst? Bed chefen om at blive sendt til kaffe-afvænning..

(a) Havde overtaget noget kode med en fejl, som opstod på min kollegas bruger men ikke på min ( på samme maskine ). Det viste sig, at den eneste forskel var, at min mailbox var "opryddet" med kun 3.200 items, hvorimod hans havde 72.000 items - nok til at Outlook langsom nok til at misse et objekt.

(b) BackTrack Linux og Knoppix har en masse gode tools og kan bruges som Live Images - hvis ikke EU får vedtaget et forbud mod udvikling og besiddelse af disse "hacker tools" :-(

Jeg er helt enig med Peter Toft i, at STÆDIGHED er en vigtig ting. Badeanden HAR virket flere gange for mit eget vedkommende ( selvom folk kigger skævt til én nogle dage efter ;-) )

Det er nu min opfattelse, at det er hamrende svært at lave en one-size fits all kursusplan ( og dermed et kursus ), som dækker alle industrier. Man ryger ud i spørgsmålet

Har programmet features som :

  1. Netværksadgang?
  2. Portadgang ( USB / RS232 / RS485 / LPT )?
  3. Hardwareadgang ( IO, Memory etc. ) ?
  4. Multithreading?

MEN!!!!! Jeg vil gerne komme med en opfordring ( hvis nogen skulle have lyst til det ) om, at nogen sætter sig ned og laver en række virtuelle maskiner ( Linux eller fx. tidsbegrænsede Windows ( licensbestemmelserne skal lige undersøges )), som indeholder nødvendige værktøjer og en række "test" kode, som fejler og skal debugges. Jeg vil gerne give en hånd til dette - og så finder vi et sted at hoste disse images.

Torben Mogensen

Her en kommentar, jeg tidligere havde til defensiv programmering: http://www.version2.dk/blog/defensiv-programmering-16897

Selv om denne kommentar primært handlede om performance, så kan man sige noget lignende om fejlsøgning: Hvis man bruger sprog med passende stærke typesystemer og garantier (såsom pointersikkerhed), så behøver man ikke værktøjer såsom Purify, Lint og Valgrind -- de fejl, som disse værktøjer finder, vil ikke kunne forekomme i et program, der oversættes uden fejl.

I et andet indlæg (http://www.version2.dk/blog/oo-dur-ikke-til-unit-test-7406) argumenterede jeg for, at sideeffekter i objekter gør det svært at lave fornuftige unit tests, så jeg vil også anbefale, at man enten undlader at bruge objekter eller i hvert fald undlader at bruge opdaterbare felter i objekter. Felter, der initialiseres ved konstruktion af objektet er O.K., men derefter skal man lade dem være. Groft sagt skal man bruge en mere funktionel kodestil.

Povl H. Pedersen

Debugging er bare en ganske almindelig problem-solving disciplin. Hvis man behersker det, så er debugging bare endnu en triviel opgave.
Personligt bruger jeg meget debugger og printf (så jeg har en ide om hvordan tingene ser ud når jeg når breakpoint, og om jeg bare skal trykke go).
Stacktraces er ofte også gode, og kan få få sekunder bruges til at få en ide om årsagen/lokationen (kræver crashing error).

Debug er simplere end programmering. Man skal ikke tænke nyt, bare forstå hvad der sker og hvad der skulle ske.

Per Hansen

Da jeg var "lille" og ny inden for programmering, debuggede jeg mine programmer meget for at finde fejl. Det tog timer.

Men efterhånden lærte jeg, at det meget bedre kunne betale sig at skrive programmerne ordentligt fra starten. Og hvis programmet ikke var ordentligt skrevet fra starten, så rydde op i det, refaktorisere det. Altså ændre programmet, så det stadig gjorde præcis det samme, men meget klarere og meget mere læseligt.

Resultatet er nu, at jeg meget sjældent debugger. Og hvis mine programmer ikke virker ved første kørsel, er fejlen ekstremt nem at finde. Ikke ved at debugge, men ved at læse programkoden én gang til. Næsten altid drejer det sig om en meget lille fejl, som springer umiddelbart i øjnene.

Mit helt store mantra er smuk kode. Programkoden skal være smuk og ekstremt letlæselig. Så virker den.

Funktioner skal være så korte som muligt, helst højst 3-5 linjer. Det skal være klart for enhver, der læser koden, hvad den gør. Ingen "smarte" tricks for at vise, hvor "god" man er til at programmere og hvor godt man kender det specifikke sprogs sære finurligheder – det straffes man for senere. Skriv så enhver kan forstå det, for så kan du også selv forstå det senere, når du læser koden igen. Hvis du endelig laver noget finurligt, så skriv meget klart i en kommentar ud for finurligheden, hvad du forventer den præcis gør. Undgå arrogance; skriv så klart at alle kan forstå det.

At programmere er på mange måder som at kommunikere. Komplekst og indviklet sprog giver fejl i kommunikationen, budskabet går ikke ordentligt igennem, ting bliver misforstået; programmet fejler. "Tal" klart og tydeligt og helt umisforståeligt. Så virker kommunikationen; så virker programmet.

Undgå de fristende forsøg på hastighedsoptimering mens du skriver koden. Sørg først for, at den virker, og virker stabilt. Hvis det viser sig, at den kører for langsomt, så find ud af, hvor flaskehalsen er, og overvej om du kan bruge en anden, hurtigere algoritme til den pågældende funktion i stedet.

Men før du omskriver funktionen til at være hurtigere, så skriv unittests til den, så du kan sikre dig, at den stadig gør det samme efter omskrivningen. Behold den gamle funktion med et andet navn, indtil den nye funktion virker og opfylder alle de samme unittests.

Man kan ofte komme ud for at skulle debugge gammel, dårligt skrevet kode. Vejen er stort set den samme: Refaktoriser den gamle kode, indtil den bliver krystalklart formuleret. Så plejer fejlen (eller fejlene) at dukke op helt af sig selv, mens man refaktoriserer. Jeg har ofte via denne metode oplevet at finde fejl, som slet ikke var blevet opdaget under de hidtidige kørsler af programmet, men som bestemt ville have vist sig på et tidspunkt.

For mig at se handler det om at skrive så klar kode, at den nærmest virker barnligt nem at forstå. Så har man det robuste, stærke program. Kunsten og håndværket består i at få det hele til at se meget enkelt og ligetil ud. Altså at forstå problemet så dybt, at det hele bliver enkelt. Og dét er svært. Det kræver at man tænker sig grundigt om, og det kræver nok også en del øvelse og erfaring.

Hvis man ser et program, som ser kompliceret ud, så er det næsten et sikkert tegn på, at det indeholder fejl. Det er nemlig så kompliceret, at vi mennesker ikke fuldstændig kan overskue det, og fejlene falder derfor ikke én umiddelbart i øjnene.

Så mit råd til, hvordan man debugger, er: Brug ikke en debugger, men læs og refaktoriser koden, indtil den står lysende klart formuleret. Så dukker fejlen helt sikkert op "af sig selv", og samtidig får man en ren og smuk kode, som andre også vil kunne læse og vedligeholde.

Pelle Söderling

Per: Grundlæggende enig i at funktioner og klasser skal holdes korte og koncise og at kode som udgangspunkt bør skrives så letforståelig som muligt.

Men det ændrer vist næppe på at vi allesammen fra tid til anden ender i debugging sessions. Den type bugs du kan afhjælpe med "smuk kode" er i høj grad de trivielle fejl - såkaldte "tanketorsk" bugs. Men ofte skyldes bugs også at vi bruger OS ressourcer, frameworks og lign. som vi ikke selv har skrevet og som enten har nogle spændende sideeffekter eller hvor det måske viser sig at vores måde at bruge dem på ikke var helt korrekt. Denne type bugs har jeg svært ved at tro du kan løse blot ved at kigge på din kode. Generelt er mange bugs svære at løse hvis man kun ser på koden og ikke ser på hvad der sker under afviklingen, hvilke data der produceres.

Som så mange andre synes jeg reelt du også går lidt uden om den varme grød, blogindlægget handler reelt ikke så meget om hvordan vi undgår debugging, som hvad gør du når du står i en debugging situation? :) Det synes jeg kunne være meget mere spændende at høre lidt om din vinkel på :)

Povl H. Pedersen

Debugging er at lede efter fejl, der ofte enten er kendte og reproducerbare, eller også alene er beskrevet ved symptomer.
Når du får en fejlmelding, så er der ikke 2.5 mio kodelinier at gennemgå. Du skal have en forståelse for programmet, og så må du kunne følge flowet der fører til fejlen. Hvis man har et problem, så er det ikke svært at gætte hvor man skal lave nogle printk (ved kernel debug) for at lokalisere fejlen. Selvfølgelig er der nogle typer af fejl som er svære at reproducere, og dermed er de også vanskelige at løse. Men her må man så bruge eksempelvis en virtuel maskine til at ændre ydre faktorer (RAM, Disk, Swap, CPU). Hvis programmet crasher i binære drivere, så må man igen tilbage og validere kald, evt hele historikken til denne driver. Det er ikke altrid man selv har lavet fejl, så må man prøvet andre workarounds.

Der er selvfølgelig også andre fejl, f.eks. memory leaks, men det er mere en optimeringsopgave end debugging. Og der er fine værktøjer til det også.

Men det er og bliver alm. problem solving skills som jeg ser det.

Kenneth Geisshirt

Når jeg finder fejl og debugger, sker det ofte vha. falsifikation. Det vil sige at jeg opbygger en hypotese om hvor eller hvad fejlen er og forsøger at vise at den er forkert. Tricket er at det kræver kun eet eksempel på at vise at en hypotese er forkert, mens selv uendelige mange eksempler ikke beviser noget. Jeg tror at Poul-Henning Kamp vil fortælle mig at han har mange asserts i Varnish af samme årsag men jeg bruger nu også ofte printf (eller hvad det kun hedder i det konkrete sprog).

http://en.wikipedia.org/wiki/Falsifiability

Pelle Söderling

imo er den videnskabelige metode uhyre ineffektiv når det kommer til debugging, selvom jeg er klar over den er ret udbredt. Det er gætværk og det er tidskrævende.

Asserts er i kategorien defensiv programmering og har imo ikke meget med hypotese-debugging at gøre - tværtimod handler det om at få brugbar og valideret information, ikke om at gætte sig til et problem og efterprøve en teori.

Beklager hvis jeg er lidt efter gætte-metoden, jeg er klar over det formentlig er den mest udbredte form for debugging, men jeg mener også den er ansvarlig for at mange udviklere bruger mere tid på debugging end på udvikling.

Og jeg mener der er brug for at uddannelserne tager det seriøst, fordi idag er det reelt op til hver af os selv at gøre os vores egne erfaringer. Jeg har oplevet udviklere der selv efter 10 års erfaring endnu ikke har lært hvordan man griber debugging af trivielle problemer an, men stadig kan sidde og bruge timer på at finde en løsning. Det er et problem som der skal mere fokus på.

Lars Madsen

Jeg har også jævnligt fundet fejl i egen defekt kode ved at forklare hvad jeg laver overfor andre. Midt i min forklaring kan jeg så stoppe med "aaah - nu forstår jeg det".

Se det er altid en god ide. Som min gamle lærer i Pålideligheds teori sagde: "Hvis du har et problem du har svært ved at løse så spørg nogen. Ikke nødvendigvis for at få svaret, men mere for at få formuleret for sig selv"

Martin Glob

Det er godt nok ved at blive en temmelig omgang akademisk pladder....

Debugning har et formål. At løse en eller flere fejl - som man enten kan man løse den eller ikke kan.

  • Forstå problemet. Fat lige hvad det er der er galt.
  • Skal der en HTR (How-To-Reproduce) til eller er det ret åbenlyst? Tryk på B og programmet smider en exception eller er det "Hvis man angiver X i felt 50 og brugeren har Y rettigheder og det er den 31. så kan brugeren ikke bestille kaffe af mærket Z" - og ja... Nogle enkelte er ramt af fejl som "Under solstormen den 28. fejlede IMU'en så magnetometeret angav en værdi med en fejl på 0.000123%"... Det er jo bare ikke der de fleste er vel?

  • Er det en design, kode eller andet-fejl?

  • Lær dine værktøjer at kende
  • Find fejlen og ret den...
  • Kan du ikke så find en der kan hjælpe dig.

Debugging er blot den anden side af at skrive kode, som det er at rette en helligdag hvis man er maler eller kunne kunne redde en skilt sovs hvis man er kok.

Bevares, der findes mange akademiske tilgange til at UNDGÅ at skulle debugge, men der er jo ikke det emnet handler om vel?

"Debugging er for mig ofte en kamp mod et ukendt problem jeg ikke har set før. Især hvis man har 2,5 million linier kode - hvilket jeg har."

Det giver jo ikke rigtig mening... Du har en fejl i dit program, men du må sgu da kunne afgrænse det til et eller andet område i dine 2,5 millioner kode - og så tage den derfra.

Det lyder nærmere som et design problem, der gør fejlsøgning besværlig...

Martin

Sune Marcher

IMHO er debugging-strategi pænt afhængig af den kodebase du sidder med, og hvem der har skrevet den... samt en blanding af de sprog og frameworks der er involverede. Jeg tvivler på der er én gylden strategi der dækker alt.

Rigtigt mange fejl jeg har siddet med har endt med at blive løst via printf-style debugging. Enten fordi der ikke har været fornuftige debugging værktøjer, eller fordi de har crashet, eller har påvirket state (threading-relaterede problemer). Selv print-statements kan påvirke determinismen i et program, men så kraftige bugs har jeg heldigvis sjældent været ude for - ikke at trawling gennem logfiler er skide skægt :)

Andre gange har par-debugging virket ret godt. Rubberduck har aldrig virket for mig, men med en menneskelig kollega har jeg haft rigtigt gode resultater (selvom kollegaen i nogle få tilfælde har sagt så lidt at det næsten kunne være rubberduck). Men det at man skal formulere sig overfor et menneske lader til at aktivere nogle andre dele af hjernen end kode/debug-mode. Om det så har været min egen kode eller noget flere år gammelt legacy man har arvet, har jeg flere gnage været ude for at dét at forklare kode til en kollega (selvom man ikke nødvendigvis har forstået koden!) har resulteret i facepalm moment før kollegaen har åbnet sin mund.

> Asserts er noget man typisk bruger for at validere pre- og postconditions samt invarianter. Hvilket
> ofte er et middel til at komme væk fra defensiv programmering.

Der er flere niveauer af pre- og post conditions. Der er deciderede programmeringsfejl, der egentligt ikke burde checkes for runtime - fejl der burde være fanget af systematiske test. Det bør kodes ind som asserts. Hvis det sker runtime, så laver vi voldsom crash-and-burn.

Derudover er der ting man må antage kan ske runtime - ting der generelt ikke /burde/ kunne ske, men sker på grund af misforståelse af APIer, brugerinput der ikke er fornuftig valideret, whatever.

I større systemer må jeg ærligt indrømme jeg synes det kan være svært at vurdere hvilke fejl der er teoretiske og skal crash-and-burn, og hvilke man kommer til at støde på hele tiden.

Arnold Christensen

Dengang jeg udviklede embedded software, var en seriel port guld værd. Det første jeg byggede var altid en consol, hvor jeg kunne styre udprint (vha makroer) og kalde testfunktioner med forskellige parametre. På et tidspunkt havde jeg bruge for at kunne finde memoryleaks, som vi havde mistankte til skete inden i en STOR klump autogenereret kode (Lavet fra noget visuelt programmerings snavs), da havde jeg en funktion der løb alle hukommelsesblokke igennem. Vores OS bruge preallokerede hukommelsesblokke. Simpelt hvis man har hukommelsen til det, makroerne kan jo fjernes ved release, men for det meste er de gode at have i til fælder af fejl...

Andre gange har jeg udviklet softwaren (i C) på en pc, og skrevet en test "main()" samtidigt med at tilføjede funktionalitet. Det viste sig at være en god ide, der var ingen nævneværdige fejl, efter det kom ned i det embeddede miljø.

Log ind eller opret en konto for at skrive kommentarer

IT Businesses