Tools voor debugging
Arnout van Kempen over rommelen in een digitale wereld.
Ons programma van vorige keer nodigde, hoop ik, uit tot experimenteren. En als je experimenteert gaan er ongelukken gebeuren, dat hoort er bij. Wie met Rust fouten maakt, krijgt van de compiler vaak al te horen wat er fout gaat en hoe dit op te lossen. En als dat niet genoeg is, biedt de taal een scala aan hulpmiddelen om fouten op te sporen. Hoe is het mogelijk dat Rust bij compileren al op fouten wijst? In essentie komt dat doordat de taal zelf bepaalde logica bevat die door de compiler wordt gecontroleerd. Het systeem van ownership en borrowing betekende dat Rust memory safety biedt en dus ook dat de compiler kan controleren of je die regels overtreedt. Maar bedenk dat dit regels van de taal zijn, niet van de computer.
In assembly kan je vrijwel geen fouten maken tegen de taal, omdat de taal geen eigen logica bevat. Alles wat leesbaar is voor de CPU is voor de assembler correcte code, ook als die nergens op slaat. Het gaat hooguit mis als je bijvoorbeeld een register wil vullen met meer dan 64 bits, of als je een instructie geeft die niet bestaat. In die gevallen geeft de assembler een foutmelding. Maar heel veel meer kan je niet doen om de assembler tot foutmeldingen te bewegen.
Als je dat combineert met het feit dat assembly nauwelijks leesbaar is en erg afhankelijk van correct commentaar dat je toevoegt, dan wordt het vinden van een fout wel erg lastig. Zo heb ik in het programma van vorige keer op enig moment bedacht dat het handig was om een ‘\n’ toe te voegen aan de cijfer-string, maar ik vergat dat de lengte van de string daarmee wel met één karakter toenam. Waar in de code mov x2, 0 stond moest daarom een mov x2, 1 staan. Als je dat over het hoofd ziet, hoe kom je daar dan achter?
Debugging in assembler is vrijwel niet te doen zonder enkele tools:
1. Goed commentaar. Klinkt flauw, is essentieel. Je moet in je broncode echt heel veel nuttig commentaar opnemen. Een aanpak kan bijvoorbeeld zijn om per brokje code te beginnen met
// 4. Zet het resultaat om naar ASCII
Dus nummeren en duidelijk aangeven wat het volgende blok voor functie heeft. En vervolgens na iedere regel code een toelichting die uitlegt wat die regel doet. En dan wel zo dat het iets toevoegt, dus niet
add x6, x6, ‘0’ // x6=x6+’0’
maar liever zoiets als
add x6, x6, ‘0’ // Zet hulpvariabele om naar ASCII
2. Objdump. Dit is een programma dat je in Linux al hebt, of anders moet installeren. Het is een tool om de inhoud van een executable of een objectfile te onderzoeken. Stel dat het programma van vorige week de naam 108 als executable heeft en je de objectfile 108.o hebt genoemd, dan kan je met deze tool beide bestanden analyseren met verschillende opties:
objdump -d 108 : geeft een disassembly van de executable.
objdump -h 108 : toont alle labels in de objectfile/executable.
objdump -t 108 : toont de symbol table.
objdump -s 108 : toont de byte-waardes van het bestand.
3.Gdb.Dit is de GNU Debugger, die we al eerder gezien hebben bij C. Met assembler is deze echt essentieel en nog makkelijk in het gebruik ook. De bruikbaarheid neemt wel enorm toe als je je code niet ‘kaal’ assembleert, maar aan de assembler opdracht geeft debug-informatie toe te voegen. Je executable wordt er groter van, dus je doet dit alleen zolang je nog aan het debuggen bent. Hiertoe voeg je de optie -g toe bij assembleren:
as -g -o 108.o 108.asm
ld -o 108 108.o
Met objdump —debugging 108|less kan je nu zien welke extra informatie in de executable is opgenomen. Maar je ervaart dat vooral als je gdb aan het werk zet.
Om de kracht van gdb te ervaren start je op met gdb 108. Je krijgt nu een inleidende tekst met als laatste regel
Reading symbols from 108…
(gdb)
Hier kan je commando’s geven aan de debugger. Als je goed wil volgen wat je programma doet adviseer ik te beginnen met
layout asm
layout reg
Hiermee krijg je in de bovenste helft van de scherm alle Xn registers met hun waarde te zien, en daaronder de assembly code van je programma. Vervolgens zet je een breakpoint direct aan het begin met
break _start
Dan start je je programma met
run
Dankzij je breakpoint wordt precies één instructie uitgevoerd. Met s ga je nu stap voor stap verder. Een cursor in het register-overzicht en een in het code-overzicht, geven precies aan waar je bent. En als je wil weten wat in het geheugen gebeurt, kan je met het commando x geheugen weergeven. Bijvoorbeeld
x/ni adres
Waarbij n het aantal weer te geven words is, i de indicator voor hexadecimaal (x), decimaal (d), instructies (i) of strings (s) is en adres is het startadres.
Met deze basisfuncties kan je exact volgen wat je programma doet en wordt het opsporen van denkfouten aanzienlijk makkelijker.
Wie mee wil doen met #klooienmetcomputers kan dat doen via GitHub. Maak een account op github.com en zoek naar Abmvk/kmc. Het account Abmvk volgen kan ook. Lezers zijn vrij te gebruiken wat ze willen en om zelf zaken toe te voegen of aan te passen, vragen te stellen of commentaar te leveren.
Gerelateerd

Einde van deze rubriek
Arnout van Kempen besluit met aflevering 112 voorlopig zijn reeks van wekelijkse bijdragen over 'rommelen in een digitale wereld': het zelf leren programmeren, verkennen...

Variabele input
Arnout van Kempen over rommelen in een digitale wereld.

Modulair bouwen
Arnout van Kempen over rommelen in een digitale wereld.

Een echt project!
Arnout van Kempen over rommelen in een digitale wereld.

Een eerste toepassing
Arnout van Kempen over rommelen in een digitale wereld.