#Klooienmetcomputers

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.

Arnout van Kempen di CCO CISA is directeur compliance & risk bij aaff, de fusieorganisatie van Alfa en ABAB. Hij schrijft op persoonlijke titel.

Gerelateerd

reacties

Reageer op dit artikel

Spelregels debat

    Aanmelden nieuwsbrief

    Ontvang elke werkdag (maandag t/m vrijdag) de laatste nieuwsberichten, opinies en artikelen in uw mailbox.

    Bent u NBA-lid? Dan kunt u zich ook aanmelden via uw ledenprofiel op MijnNBA.nl.