Introduktion till debugging med GDB

Relevanta dokument
Kompilering och exekvering. Föreläsning 1 Objektorienterad programmering DD1332. En kompilerbar och körbar java-kod. Kompilering och exekvering

Föreläsning 2: Debugging

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

Tentamen *:58/ID100V Programmering i C Exempel 3

Objektorienterad programmering i Java I. Uppgifter: 2 Beräknad tid: 5-8 timmar (OBS! Endast ett labbtillfälle) Att läsa: kapitel 5 6

Programmering i C++ Kompilering från kommandoraden

Programmering, grundkurs, 8.0 hp HI1024, HI1900 etc., Tentamen TEN1. Måndagen den 10 januari 2011,

Poster ( structar ) Postdeklarationer

F4. programmeringsteknik och Matlab

Pekare och arrayer. Indexering och avreferering

Funktionspekare, inledning: funktionsanropsmekanismen. Anrop via pekare

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Python. Python är, som Scheme, ett interpreterat språk men det finns kompilatorer för Python.

prog: prog.c gcc -g -Wall prog.c /usr/local/bib/c/58an/modules/modul.o -o prog

Python. Python är, som Scheme, ett interpreterat språk men det finns kompilatorer för Python.

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Här beskrivs Eclipse, den programutvecklingsmiljö som utnyttjas i programmeringskurserna. Mera information finns på:

Programmeringsteknisk översiktskurs för yrkeshögskoleprogram

Kompilera och exekvera Javakod

Programmering I Tobias Wrigstad fredag, 2009 augusti 28

Emacs. Eric Elfving Institutionen för datavetenskap (IDA) 22 augusti 2016

*Pekarvärden *Pekarvariabler & *

Laboration 1. "kompilera"-ikonen "exekvera"-ikonen

Programmeringsteknik med C och Matlab

Eclipse. Avsikt. Nu ska ett fönster liknande figuren till höger synas.

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Felsökning. Datatyper. Referenstyper. Metoder / funktioner

Föreläsning 2: Avlusning och antilustekniker

Värmedistribution i plåt

Inledande programmering med C# (1DV402) Ditt första C#-program med Visual Studio

Laboration 1. En första bekantskap med C++ Namn: Godkänd den: Rest:

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

Labora&on 2 Funk&oner, if och loop övningar/uppgi:er

Code-Lite tutorial ( /RoJ)

Tentamen i TDP004 Objektorienterad Programmering Praktisk del

Det finns många flaggor till g++,

Planering av ett större program, del 2 - for och listor. Linda Mannila

Funktioner forts. F3: Funktioner (kap. 5) Parametrar. findgear.m forts

Eclipse en handledning

Föreläsning 2 Programmeringsteknik och C DD1316. Mikael Djurfeldt

Programsystemkonstruktion med C++: Övning 1. Karl Palmskog september 2010

Frekvenstabell över tärningskast med C#

Introduktion C-programmering

Laboration 3 HI1024, Programmering, grundkurs, 8.0 hp

Att använda pekare i. C-kod

Deluppgift 9 Installera Pintos

Planering Programmering grundkurs HI1024 HT 2014

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

Repetition C-programmering

1 Logga in 1. 2 Byta lösenord 1. 3 Hemkatalog 1. 4 Unixintroduktion Viktiga kommandon Läsa 3

NetBeans 5.5. Avsikt. Projektfönster

PC-teknik, 5 p LABORATION ASSEMBLERINTRODUKTION

Inledning. Vad är ett datorprogram, egentligen? Olika språk. Problemlösning och algoritmer. 1DV433 Strukturerad programmering med C Mats Loock

Laboration 1. "kompilera"-ikonen "exekvera"-ikonen

Laboration: Whitebox- och blackboxtesting

Frekvenstabell över tärningskast med C#

Introduktionslaboration

Klassdeklaration. Metoddeklaration. Parameteröverföring

Funktioner. Linda Mannila

Objektorienterad programmering i Java I

Funktionens deklaration

TDIU01 - Programmering i C++, grundkurs

Vad händer när man kör ett program? Program och processer. Funktionsanrop. Avsluta programmet

GPT föreläsning 8. Förra veckan: Man kan ta tiden på en sorterad teckensträng Förra gången: Problemlösning på lägre nivå kan sortera funktioner

Objektorienterad programmering. Fält som funktionsresultat. Mer om fält: att uppdatera ett parameterfält. Kontrast: Parametrar av primitiv typ

Static vs Dynamic binding Polymorfism. Objekt-orienterad programmering och design (DIT953) Niklas Broberg, 2018

TUTORIAL: SAMLING & KONSOLL

Mathematica. Utdata är Mathematicas svar på dina kommandon. Här ser vi svaret på kommandot från. , x

IDA kursmaterial Informationsblad make. make

Lab5 för prgmedcl04 Grafik

Introduktion till användning av linux-servern sledge och några övningsuppgifter

Introduktion till programmering SMD180. Föreläsning 3: Funktioner

Programmering, grundkurs, 8.0 hp, Elektro, KTH, hösten Programmering: att instruera en maskin att utföra en uppgift, kräver olika språk:

Introduktion till arv

Dagens program. Programmeringsteknik och Matlab. Viktiga datum. Ett första exempel. Programmall, vad behöver vi i ett javaprogram?

Programmering av inbyggda system. Kodningskonventioner. Viktor Kämpe

2 Eclipse en handledning

TDIU01 - Programmering i C++, grundkurs

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Muddy. Funktioner / metoder. Punktnotation. Evalueringsordning

F5: Högnivåprogrammering

F5: Högnivåprogrammering

TDDC76 - Programmering och Datastrukturer

Hej Då, Karel! Programmering. Vårt första Javaprogram. hh.se/db2004. Java. Grundtyper, variabler och arrayer

Övning 1. Datateknik A, Java I, 5 poäng. Att ladda hem och installera Java Att skriva ditt första Javaprogram Att kompilera och exekvera ditt program

Command line argumenter. Objektorienterad Programmering (TDDC77) Vad blir resultatet? Nu då? Ahmed Rezine. Hösttermin 2016

Att skriva till och läsa från terminalfönstret

Objektorienterad Programmering (TDDC77)

Introduktion till programmering SMD180. Föreläsning 4: Villkor och rekursion

KPP053, HT2016 MATLAB, Föreläsning 1. Introduktion till MATLAB Skript Inläsning och utskrift av variabler Ekvationssystem Anonyma funktioner

Program. Kapitel make Program Interpreterande och kompilerande program

Strängar. Strängar (forts.)

Java: Utvecklingsverktyg, datatyper, kontrollstrukturer

Objektorienterad programmering Föreläsning 2

Dagens program. Programmeringsteknik och Matlab. Administrativt. Viktiga datum. Kort introduktion till matlab. Övningsgrupp 2 (Sal Q22/E32)

Handbok KXSLDbg. Keith Isdale Översättare: Stefan Asserhäll

Så här skriver du ditt första program i C++

Alla filer som bearbetar PHP script ska avslutas med ändelsen.php, exempelvis ska en indexsida till en hemsida heta index.php

NetBeans 7. Avsikt. Projektfönster

Övningsuppgifter kapitel 8

Agenda. Arrayer deklaration, åtkomst Makron Flerdimensionella arrayer Initiering Strängar Funktioner och arrayer. Övningar nu och då

TENTAMEN OOP

Transkript:

Jozef Swiatycki Sid 1(11) *:58/ID100V HT07 Innehållsförteckning Inledning...1 Läsa mer om GDB...2 Programexempel...2 Förberedelse för debugging...2 Radnumrering av källkod...3 Grundläggande GDB-kommandon...3 Starta GDB...3 Starta programexekveringen...4 Avsluta GDB...4 Brytpunkter...4 Fortsätta exekveringen...4 Skriva ut variabelvärden...5 Flytta sig inom anropsstacken...5 Skriva ut anropsstacken...6 Skriva ut en länkad lista...6 Radvis exekvering...7 Exekvera resten av en funktion...7 Lokalisering av fel...8 Lokalisering av exekveringsavbrott...8 Lokalisering av oändliga loopar...9 Återstående fel i wrong.c...9 Kommandoradsargument vid debugging...10 Undersökning av void *-utpekade objekt...10 Exempelprogrammen right.c och wrong.c...11 Inledning Debugging (man har föreslagit "avlusning" som svensk term, den har dock inte blivit allmänt accepterad) utgör en del av programtestning, där tester först visar förekomsten av fel (programmets output överensstämmer inte med den förväntade), varefter felen måste lokaliseras och avhjälpas. Det finns olika tekniker för denna lokalisering inklusive analys av källkoden av programmeraren eller en grupp av programmerare och provkörningar av program med olika indata och med inlagda spårutskrifter. Ett mycket viktigt verktyg i samband med provkörningar är s.k. debuggers ("avlusningsverktyg") som är program som tillåter exekvering av det testade programmet under kontroll av debuggern, med möjlighet att observera det testade programmets exekveringsföljd, värden i dess primärminnesceller m.m. Det exekverbara programmet är ju uttryckt i maskininstruktioner och källkodens variabel- och funktionsnamn är ersatta med primärminnesadresser. För att möjliggöra kommunikation med programmeraren med användning av källkodens radnummer och variabel- och funktionsnamn lägger kompilatorer och länkare in omfattande symboltabeller som innehåller information om avbildning av källkodsrader på motsvarande maskininstruktionsföljder och en avbildning av variabel- och funktionsnamn på primärminnesadresserna. Debugging med användning av källkodens symboler och radnummer kallas symbolisk debugging. Här introduceras debuggern GDB som är GNU-debuggern för program kompilerade med kompilatorn GCC.

Jozef Swiatycki Sid 2 *:58/ID100V HT07 Läsa mer om GDB Tillsammans med detta häfte delas ut ett tvåsidigt referenskort med samtliga GDBkommandon och mycket kortfattad beskrivning av varje kommando. Kortet finns även för nedladdning och utskrift (Postscript-format) på momentets webbsida (Kursmaterial). På momentets webbsidor (Verktygsmanualer >> GDB) finns en länk till den fullständiga GDB-manualen "Debugging with GDB" i HTML-format. GDB har kommandot help, som ger en lista med olika kommandokategorier. Kommandot help kategori ger en lista av kommandon inom denna kategori med en kort beskrivning av varje kommando. Kommandot help kommando ger en mer fullständig beskrivning av kommandot. Det finns olika användargränssnitt till GDB. Här beskrivs det grundläggande kommandogränssnittet. GDB kan dock även köras inifrån Emacs, se avsnittet "Using GDB under GNU Emacs" i GDB-manualen på webben. Det finns även ett antal grafiska användargränssnitt till GDB under Linux, främst ddd som startas med kommanot ddd programnamn. Kommandot man ddd ger mer information, liksom webbsajten http://www.gnu.org/software/ddd/. Programexempel Denna introduktionen ges i form av ett par exempel: programmen right.c och wrong.c, som återges sist i häftet. Båda programmen skapar en länkad lista av poster bestående av ett nummer, en sträng och en pekare till nästa post. Strängarna till posterna tas från en array av strängar (för att undvika tidsödande inmatning under demonstrationen av debuggern). Programmet right.c är inte behäftat med (avsiktliga) fel, men har inte utskrift av listan implementerad, det används för att visa grundläggande GDB-kommandon (t.ex. hur man kan undersöka programvariabler och följa exekveringen rad för rad). Programmet wrong.c är behäftat med några fel som är markerade i källkoden, det används för att demonstrera olika felsökningssituationer. Dessutom används programmet echoln.c för att vissa hur kommandoradsargument anges under debugging (detta program är inte intressant nog för att återges i häftet). Dessa program finns på kursens webbsidor, de kan med fördel kopieras till egen katalog och användas tillsammans med detta häfte för övning i fall man missar presentationen. Förberedelse för debugging För att kompilatorn och länkaren skall inkludera symboltabellen (och alltså möjliggöra symbolisk debugging) måste man både vid kompilering och länkning ge flaggan -g: gcc -c -g right.c gcc -g right.o -o right eller i ett steg: gcc -g right.c -o right Obs att det kompilerings- och länkningskommando som på vårt Linux-system genereras av programmet make (och utan att man själv anger hur make skall generera kommandon, vilket

Jozef Swiatycki Sid 3 *:58/ID100V HT07 kommer att visas på Verktygspresentation 2) inte innehåller denna flagga. Tillsvidare kan man alltså inte använda det bekväma make-kommandot om man vill kunna debugga sina program. Om man glömt att kompilera eller länka sitt program med flaggan -g kan man fortfarande debugga sitt program, men man måste göra det på maskinkodsnivå. Detta rekommenderas inte, men GDB kommer att starta utan att kunna "köra programmet radvis" eller förstå variabel- eller funktionsnamn. Man ser dock att symbolisk info inte är inläst genom att GDB meddelar att "...(no debugging symbols found)...", man bör då lämna GDB, kompilera och länka om programmet med -g-flaggan och starta GDB igen. Radnumrering av källkod GDB identifierar källkodsrader med radnummer. Det kan alltså vara bra att lätt kunna lokalisera källkodsrader med hjälp av deras radnummer. GDB har ett kommando för utskrift av radnumrerade portioner av källkoden på skärmen. Kommandot heter list (kan förkortas till l), som default skriver det ut 10 rader, nästa kommando list skriver då 10 rader till. Man kan ge kommandot ett radnummer, t.ex. list 53 varvid kommandot skriver ut 10 rader omgivande denna rad, eller ett radnummerintervall, t.ex. list 53, 59 varvid GDB skriver ut rader from 53 tom 59. I Emacs kan man se radnummer där markören befinner sig vid bokstaven L på mode-raden. Man kan även ge Emacs-kommandot M-x goto-line RETURN radnummer (tryck på ALT och x samtidigt, skriv in kommandot goto-line, tryck på RETURN, mata in radnumret och tryck på RETURN), varvid markören hoppar till denna rad (detta kommando kan även fås genom menyvalet Edit >> Go To >> Goto Line... ). Slutligen kan man vid utskrift av textfil med kommandot lpp begära radnumrering med flaggan -N: lpp -N right.c Grundläggande GDB-kommandon Starta GDB GDB startas med kommandot gdb programnamn där programnamn är namnet på den exekverbara filen. Källkoden borde dock finnas på samma katalog för att GDB skall kunna visa källkodsrader under debugging. GDB svarar så här: GNU gdb 2002-04-01-cvs Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-linux"... Texten på sista raden är GDBs prompt som innebär att GDB väntar på kommandon.

Jozef Swiatycki Sid 4 *:58/ID100V HT07 Starta programexekveringen Programexekvering startas med kommandot run (kan förkortas till r), men om man ger detta kommando direkt så exekveras programmet från början till slut som vanligt (det finns ibland anledning att göra så, se avsnittet om lokalisering av exekveringsfel resp. oändliga loopar). Vill man observera programexekveringen bör man ange att exekveringen skall stoppas någonstans i programmet genom att sätta in s.k. brytpunkter. Avsluta GDB Innan vi tittar på brytpunkter kan vi nämna hur man lämnar GDB: kommandot heter quit (kan förkortas till q). Brytpunkter En brytpunkt sätts i en viss funktion eller på en viss källkodsrad och innebär att exekveringen av programmet kommer att avbrytas just när funktionen blivit anropad (men innan dess första sats har blivit exekverad) eller just innan exekveringen av den angivna källkodsraden, sedan kan man ge nya GDB-kommandon och fortsätta exekveringen. Sätta brytpunkt i en viss funktion: kommandot heter break funktionsnamn (kan förkortas till b funktionsnamn), t.ex. b dohitta Efter detta kommando ger GDB utskriften Breakpoint 1 at 0x804880e: file right.c, line 53. GDB numrerar brytpunkter, denna brytpunkt har nummer 1 (vissa kommandon kräver brytpunktsnummer, men vi kommer inte att titta på dessa kommandon i denna introduktion). Därefter följer den hexadecimala adressen till instruktionen där exekveringen kommer att avbrytas, även den informationen vi kan ignorera. Sedan följer filnamnet och radnummer i källkoden till den sats inför vilken exekveringen kommer att avbrytas. Om man nu ger kommandot run så kommer exekveringen att starta och sedan avbrytas när funktionen dohitta har anropats, med utskriften: Breakpoint 1, dohitta (first=0x8049ca0) at right.c:53 53 printf("vem: "); GDB visar att exekveringen avbröts vid brytpunkt 1, i funktionen dohitta, inom parentes anges värden som funktionens argument hade vid detta anrop (i detta fall är det en pekare, så dess värde är en hexadecimal primärminnesadress, som inte ger oss så mycket information. Ibland kan det dock vara värt att titta på sådana värden, t.ex. om man vill se om två pekare pekar på samma sak - samma hexadecimala adress betyder att de pekar på samma sak). Därefter visar GDB vilken sats som står i tur att utföras och inväntar nya kommandon. Om man vill ange brytpunkt på en viss källkodsrad istället så är kommandosyntaxen break radnummer, t.ex. b 53 Om funktionen eller källkodraden finns i en annan fil än den aktuella kan funktionsnamnet eller radnumret kvalificeras med källkodsfilens namn: b list.c:dohitta b list.c:53 Fortsätta exekveringen Nu kan man ge nya kommandon (sätta nya brytpunkter, skriva ut variabelvärden o.s.v.) och sedan fortsätta exekveringen. Kommandot för att fortsätta exekveringen efter avbrott heter continue (kan förkortas till c).

Jozef Swiatycki Sid 5 *:58/ID100V HT07 Skriva ut variabelvärden Om man vill se vilka värden variabler har vid detta tillfälle kan man ge kommandot print (kan förkortas till p). I funktionen dohitta, där vi nu "står", finns en variabel vem, man kan skriva ut dess värde med p vem varvid GDB svarar något i stil med: $1 = "À\227\0@\200p\023@\0P\001@±a\004@H\236\023@" Variabeln vem är en array av char och den har ännu inte blivit initierad, så dess innehåll är några "skräptecken". GDB numrerar även värden som den visar, därav inledningen med $1. Vi kommer inte att behöva dessa nummer, men väl en notation för "det senast utskrivna värdet" (se avsnittet "Skriva ut en länkad lista"), så det är bra att veta att GDB kommer ihåg de utskrivna värdena. På samma sätt kan globala data skrivas ut, t.ex. om vi vill veta värdet av den globala antal: p antal $2 = 8 För att se värden för existerande variabler i andra funktioner krävs det dock mer (se nästa avsnitt). Flytta sig inom anropsstacken Funktionen dohitta är anropad från main och i main finns en variabel som heter list (denna variabel skickades som argumentet first till funktionen dohitta). Variabler i funktionen main existerar, main har ju inte terminerat utan bara avbrutits i avvaktan på att dohitta skall bli klar. Säg att vi vill se värdet av variabeln list: p list No symbol "list" in current context. GDB "ser" vid varje ögonblick de symboler som är deklarerade enligt C:s regler för deklarationsvidd. I funktionen dohitta syns denna funktions lokala variabler och de globala variablerna, däremot syns inte lokala variabler i andra funktioner, även om dessa funktioner är aktiva och deras variabler existerar och ligger på stacken. Man kan dock flytta GDBs "synvinkel" upp (till tidigare anropad funktion) och ner (till senare anropad funktion, detta kan givetvis endast göras om man har flyttat synvinkeln upp innan). Kommandot för att flytta synvinkeln upp heter up, för att flytta synvinkeln ner down. Så för att se variabeln list från funktionen main kan man göra up #1 0x0804893e in main () at right.c:76 76 case 'H': dohitta(list); break; p list $4 = (Post *) 0x8049ca0 Detta visar nu värdet på pekarvariabeln list och även att denna adress betraktas som pekare till Post. Obs att man nu kan se att list:s värde är lika med argumentet first:s värde (som visades när funktionen avbröts vid brytpunkten). Obs att detta inte ändrar exekveringsordningen, programmet är fortfarande avbrutet i funktionen dohitta, vi har bara ändrat vilka symboler GDB ser just nu.

Jozef Swiatycki Sid 6 *:58/ID100V HT07 Skriva ut anropsstacken Man kan se anropsstacken med kommandot backtrace (kan förkortas till bt): bt #0 dohitta (first=0x8049ca0) at right.c:53 #1 0x0804893e in main () at right.c:76 Stacken visas "upp och ner", utskriften visar att först anropades main och från main på rad 76 anropades dohitta (med argumentvärdet inom parentes), denna funktion är avbruten på rad 53. Vi kommer att behöva detta kommando senare (se avsnittet "Lokalisera exekveringsavbrott"). Skriva ut en länkad lista Säg att vi vill titta på den länkade listan. Om vi har flyttat GDBs synvinkel upp på stacken till main kan vi flytta den tillbaka till dohitta och be om utskrift av argumentet first: down #0 dohitta (first=0x8049ca0) at right.c:53 53 printf("vem: "); p first $5 = (Post *) 0x8049ca0 Detta säger oss inte så mycket, men print-kommandot accepterar alla syntaktiska åtkomstkonstruktioner som finns i C, så för att avreferera pekaren skriver man * framför: p *first $6 = {nr = 0, data = 0x8049cb0 "STEFAN", next = 0x8049cc0} Detta säger oss mer, strukten som pekas ut av first har alltså dessa värden. Obs att char * - värden skrivs ut med både adressen och den utpekade strängen! Vill vi titta på nästa post i listan kan vi skriva: p *first->next $7 = {nr = 1, data = 0x8049cd0 "HUGO", next = 0x8049ce0} Detta blir dock senare omständigt: för att skriva tredje posten för man skriva p *first->next->next o.s.v. Det är här som historiken över utskrivna värden kommer till hjälp. Det senast utskrivna värdet kan refereras med symbolen $ (dollar). Vid utskrift av listan vill vi hela tiden skriva ut det senast utskrivna värdet utökat med ->next, så vi kan skriva printkommandot så här: p *$->next och om vi upprepar detta kommando så blir ju det senast utskrivna värdet nästa post och upprepning av samma kommando kommer att ge oss nästa post o.s.v. Eftersom GDB upprepar det senast givna kommandot när man bara trycker på RETURN så kan man skriva ut hela listan (en post i taget) med kommandona: p *first $8 = {nr = 0, data = 0x8049cb0 "STEFAN", next = 0x8049cc0} p *$->next $9 = {nr = 1, data = 0x8049cd0 "HUGO", next = 0x8049ce0} och därefter räcker det med att bara trycka på RETURN så får man utskrift av nästa post.

Jozef Swiatycki Sid 7 *:58/ID100V HT07 Radvis exekvering I många situationer kan det vara till hjälp att följa programexekveringen en källkodsrad i taget. Det finns två kommandon för att exekvera nästa källkodsrad (eller snarare motsvarande maskininstruktioner) och sedan avbryta exekveringen och vänta på kommandon: next (kan förkortas till n) och step (kan förkortas till s). Att det finns två sådana kommandon beror på att nästa källkodsrad kan innehålla ett funktionsanrop och att man i så fall kan vilja att hela funktionen utförs innan man avbryter exekveringen eller att den stegvisa exekveringen går in i funktionen och stannar där. Kommandot next utför i en sådan situation funktionen utan avbrott medan kommandot step stegar in i funktionen. Säg att vi vill följa exekveringen av programmet right.c radvis. Vi startar GDB med gdb right Kommandona next och step kan endast användas i ett program vars exekvering har startats. Vi kan å andra sidan inte bara ge kommandot run för då kommer hela programmet att utföras utan avbrott. Så en mycket vanlig start av en debugging är att man ger följande två kommandon: b main Breakpoint 1 at 0x80488ae: file right.c, line 63. run Starting program: /amd/blt/home1/mars/dsv/jozef/c/debug/right Breakpoint 1, main () at right.c:63 63 Post *list=makelist(); Man sätter alltså en första brytpunkt i main (där exekveringen ju börjar) och därefter startar exekveringen av programmet. Nu kan man ge kommandot next om man vill utföra hela makelist-anropet och stanna innan nästa rad i main eller kommandot step om man vill gå in i makelist och stanna innan dess första sats. Obs att upprepning av senaste kommandot vid tryckning på RETURN gör att man efter ett första next eller step bara behöver trycka på RETURN så länge man vill fortsätta till nästa rad. Exekvera resten av en funktion Säg att vi går in i funktionen makelist: step makelist () at right.c:29 29 Post *first=null, *last, *ny; och då inser att det var onödigt och vi vill inte behöva stega oss igenom hela funktionen, däremot vill vi stanna innan nästa sats i main. Ett sätt att göra det på vore att sätta en brytpunkt på nästa sats i main och ge kommandot continue, men ett bättre sätt är att ge kommandot finish (kan förkortas till fini), som exekverar funktionen tills den terminerar och stannar efter anropet. Funktionens returvärde skrivs ut på skärmen: fini Run till exit from #0 makelist () at right.c:29 0x080488b3 in main () at right.c:63 63 Post *list=makelist(); Value returned is $1 = (Post *) 0x8049ca0 Detta sätt behövs bl.a. om man vill stega in i en funktion vars argument är ett funktionsanrop, ta t.ex. satsen if (tmp=find(first, strtoupper(vem))) Om vi vill stega in i funktionen find måste kommandot bli step, men detta kommando kommer att stega in i funktionen strtoupper först. Inne i strtoupper ger man sedan kommandot finish, därefter ger man kommandot step igen och hamnar i find.

Jozef Swiatycki Sid 8 *:58/ID100V HT07 Lokalisering av fel Lokalisering av exekveringsavbrott Vid försök till exekvering av programmet wrong.c får man bara den korthuggna utskriften Segmentation fault Detta innebär att det någonstans i programmet finns adresseringsfel. Om man låter programmet exekveras under kontroll av GDB får man mer hjälp: gdb wrong GNU gdb 2002-04-01-cvs Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-linux"... run Starting program: /amd/blt/home1/mars/dsv/jozef/c/debug/wrong Program received signal SIGSEGV, Segmentation fault. 0x080486e6 in makelist () at wrong.c:33 33 ny->nr=i; Detta visar att felet inträffat på rad 33 i programmet, i funktionen makelist. Studerar man källkoden upptäcker man att pekaren ny inte har fått något värde - vi har glömt att allokera utrymme till posten. Man får lämna GDB, rätta programmet (allokera utrymme till posten och låta pekaren ny peka på detta utrymme), kompilera om programmet och försöka igen. Nästa försök slutar lika illa. Vi startar GDB och ger kommandot run, men denna gång är inte utskriften lika hjälpsam: run Starting program: /amd/blt/home1/mars/dsv/jozef/c/debug/wrong Program received signal SIGSEGV, Segmentation fault. 0x40095134 in strcpy () from /lib/libc.so.6 Felet inträffar inne i en biblioteksfunktion (strcpy), det beror säkert på att vi skickar fel argumentvärden till strcpy, men hur skall vi veta varifrån i vårt program funktionen anropades? Det är här som kommandot backtrace (bt) för utskrift av anropsstacken är till hjälp: bt #0 0x40095134 in strcpy () from /lib/libc.so.6 #1 0x0804871e in makelist () at wrong.c:35 #2 0x0804887f in main () at wrong.c:69 Detta visar att strcpy anropades från makelist på rad 35. Vi tittar i källkoden och upptäcker att vi glömt att allokera utrymme till strängen som skickas till strcpy för kopiering av strängvärdet. Vi får avsluta GDB, ändra i källkoden, kompilera om och försöka igen...

Jozef Swiatycki Sid 9 *:58/ID100V HT07 Lokalisering av oändliga loopar Efter senaste ändring fungerar programmet mycket bättre. Men när vi prövar kommandot H till programmet och anger något namn så händer inget mer, programmet "hänger sig" och måste brytas med CTRL-C. Vi misstänker en oändlig loop, men hur hitta var den inträffar? Om man exekverar under GDBs kontroll och bryter med CTRL-C avbryts exekveringen på det ställe där den råkade befinna sig och GDB meddelar var man är och inväntar kommandon: gdb wrong GNU gdb 2002-04-01-cvs Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-linux"... run Starting program: /amd/blt/home1/mars/dsv/jozef/c/debug/wrong Du kan ge följande kommandon: A - för att skriva ut alla H - för att hitta en viss Q - för att avsluta Kommando> h Vem: stefan (Här "hänger sig" programmet och jag avbryter med CTRL-C) Program received signal SIGINT, Interrupt. 0x08048812 in find (first=0x8049c78, str=0xbffffa38 "HUGO\n") at wrong.c:51 51 if (strcmp(tmp->data, str)==0) Tydligen befinner sig programmet på rad 51, i loopen som skall gå igenom listan och jämföra varje posts medlem data med den sökta strängen. Här kan man skriva ut värdet på variabeln tmp (som är den hjälpvariabel som skall flyttas i varje varv i loopen tills hela listan är genomgången eller den sökta posten har hittats) och följa exekveringen några varv i loopen med hjälp av kommandot next. Ganska snart inser man att man loopar i for-satsen utan att tmp förändras... Man misstänker alltså att for-satsen är felaktig, framför allt modifieringsuttrycket. Och studerar men det närmare ser man att vi inte ändrar variabeln tmp (det står bara tmp->next istället för tmp=tmp->next). Återstående fel i wrong.c Det återstår några fel i demoprogrammet wrong.c. För att lokalisera dessa fel måste man följa programexekveringen och undersöka variablers värden, vilket görs med kommandon som redan tagits upp. Arbetsgången lämpar sig inte för en skriftlig beskrivning men demonstreras (om tiden tillåter) på Verktygspresentation 1.

Jozef Swiatycki Sid 10 *:58/ID100V HT07 Kommandoradsargument vid debugging Om man vill debugga program som tar kommandoradsargument så kan inte argumenten skrivas när man startar GDB (de kommer att tas som argument till GDB, inte till det testade programmet). Istället skall argumenten skrivas vid run-kommandot till GDB: gdb echoln GNU gdb 2002-04-01-cvs Copyright 2002 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-linux"... run hej hopp din ödla Starting program: jozef/c/debug/echoln hej hopp din ödla /amd/blt/home1/mars/dsv/jozef/c/debug/echoln hej hopp din ödla Program exited normally. Undersökning av void *-utpekade objekt Vi kommer under momentet *:58 att bygga återanvändbara datastrukturer som skall kunna hantera data av godtycklig typ. Sådana datastrukturer konstrueras i C med hjälp av void *- pekare, som kan peka ut vad som helst men inte får avrefereras. Om man debuggar en sådan modul och begär utskrift av det pekaren pekar ut svarar GDB: p *vpek Attempt to dereference a generic pointer. Men vid debugging av programmet är ju modulen med den återanvändbara datastrukturen sammanlänkad med ett tillämpningsprogram och vi vet under debuggingen vilken typ av data pekaren pekar ut (eller snarare vad den borde peka ut). Man kan då med ett "type cast" berätta för GDB hur den skall tolka de utpekade data, säg att vi tror att pekaren pekar ut ett objekt av typen Post: p *(Post *)vp $2 = {nr = 1, data = 0x8049cc8 "HUGO", next = 0x0}

Jozef Swiatycki Sid 11 *:58/ID100V HT07 Exempelprogrammen right.c och wrong.c Exempelprogrammen finns på kursens webbsidor under rubriken "Övrigt".