Vidare bör nämnas att benämningen DDT nedan inte har med något välkänt och förbjudet bekämpningsmedel att göra, utan står för Dynamic Debugging Technique. Begreppet avlusning (debugging) kommer från en av de första matematikmaskinerna MARK i USA, där vid ett tillfälle en skalbagge fastnat på ett av de elektromekaniska reläerna, varför maskinen inte längre fungerade. I den verkliga loggen står det dock "moth", vilket på svenska (från brittisk engelska) blir mal, mott eller nattfjäril. En bild på denna mal finns på nätet.
På amerikansk engelska heter skalbagge "bug", men översatt från brittisk engelska till svenska blir "bug" i stället vägglus. På brittisk engelska heter skalbagge i stället "beetle".
Tekniken DDT kan användas för "on-line" testning av bl a FORTRAN-program. Sedan användarprogrammet blivit assemblerat eller kompilerat kan det laddas med DDT. Genom att ge kommandon till DDT kan användaren sätta brytpunkter där exekveringen tillfälligtvis avbryts.
Detta ger möjlighet att testa ut program genom att enkelt inspektera godtyckliga programvariabler i brytpunkterna. Variabler och källkod kan även modifieras utan att omkompilering behöver göras.
X = A X = BHär gäller att variabeln X först tilldelas värdet A, för att sedan omedelbart tilldelas värdet B. Man får således aldrig någon nytta av att X först fick värdet på A. Det är därför sannolikt att det rätta programavsnittet i stället skall vara någonting som
X = A Y = BEn annan anomali är
SUBROUTINE SUB(X,Y,Z) Z = Y + WHär är W odefinierat när det skall användas för att bilda Z. Menade författaren X i stället för W, eller W i stället för X, eller skall W vara i ett COMMON-block?
Som dessa exempel antyder kan vanliga programmeringsfel orsaka anomalier i dataflödet. Sådana fel är stavfel, namnförväxling, felaktig parameteröverföring i proceduranrop, saknade satser och så vidare.
Om man finner en anomali i dataflödet är detta dock inget säkert tecken på ett fel, bland annat kan det vara så att just den betraktade flödesvägen ej kan exekveras. Man kan i viss mån undvika sådana "fel" genom att utnyttja symbolisk exekvering, men sådan exekvering är mycket dyrbar.
Dataflödesanalysen ger förutom upptäckten av anomalier i flödet även värdefull information för dokumentationen av programmet. Den ger information om vilka variabler som får (returnerar) värden vid ett proceduranrop, eller som ger (tilldelar) värden till den anropade proceduren. Den identifierar kopplingen med COMMON och EQUIVALENCE. Den identifierar avsnitt av koden där vissa variabler ej används, liksom i vilken ordning procedurerna anropas.
När en sats i ett program exekveras påverkas förekommande variabler på flera sätt, av vilka vi vill särskilja använda (REFERENCE), tilldela (DEFINE) och glömma (UNDEFINE). När vid exekveringen av en sats värdet på en viss variabel erfordras säger vi att den användes i satsen. Om den i stället ges ett värde säger vi att den tilldelas. Slutligen förekommer det att en variabel tappar sitt värde vid (efter) exekveringen av en viss sats, då säger vi att den glömmes. Som förkortningar för dessa begrepp användes "r", "d" och "u".
När glömmer en variabel sitt värde? I ett blockstrukturerat språk (Algol) så inträffar detta varje gång man går ut från ett inre block, då försvinner alla lokala variabler. På motsvarande sätt gäller i Fortran att uthopp från ett underprogram gör att värdena på alla lokala variabler försvinner, med undantag av de fall då variabeln sparats med SAVE eller är tilldelad med en DATA-sats och ej förändrats.
I Fortran-satsen
A = B + Canvändes B och C, medan A tilldelas, medan i
I = I + 1variabeln I både användes och tilldelas. I satsen
A(I) = B + 1.0användes I och B, medan A(I) tilldelas.
I Fortran 66 gäller att DO-slingans index glöms (blir odefinierat) när DO-slingan är tillfredsställd. Detta har dock ändrats till det bättre i och med senare versioner (Fortran 77 och Fortran 90). DO-slingans index har nämligen numera sitt nästa värde som slutvärde. Ett annat fall av att en variabel blir odefinierad är när den tilldelas ett odefinierat värde, till exempel i Fortran 66
X = 1.0 X = SIGN(X,0.0)eller i Fortran 77
X = 1.0 X = AMOD(2.0,0.0)Det är illustrativt att betrakta följande avsnitt ur ett Fortran 66 program.
DO 10 K = 1, N X = X + A(K) IF (X .LE. 0.0 ) GOTO 20 Y = Y + A(K)**2 10 CONTINUE 20 WRITE (*,*) KDet intressanta att notera här är att DO-variabeln K ej är odefinierad när satsen X = X + A(K) exekveras efter CONTINUE, men att K är odefinierad då skrivsatsen exekveras efter CONTINUE men definierad då den exekveras efter GOTO-satsen. Till och med Fortran 66 gällde nämligen att DO-indexet vid normal utgång, dvs från en fullständigt genomlöpt DO-slinga, var odefinierat, medan från och med Fortran 77 det i stället har nästa värde. I ovanstående exempel har K således värdet N+1 i fallet fullständigt genomlöpt slinga, dvs det fall som var odefinierat i Fortran 66.
Ett annat problem orsakas av vektorer och matriser. För ett enkelt fall som
B = A(1) + 1.0är det inget problem att se att det är det första elementet i A som används, men i
B = A(K) + 1.0är det mycket svårare, till och med omöjligt i det fall att K just har lästs in. För att klara av detta problem kan man helt enkelt göra en förenkling på så sätt att man jämställer alla element i en vektor eller matris, ändras ett så ändras alla vad gäller dataflödesanalysen.
Vi återgår nu till de tidigare införda förkortningarna "r", "d", och "u", och använder följder av dessa bokstäver att betrakta hur en viss variabel genomlöper ett program.
För variabeln A i
A = A + Bgäller "rd", medan för B gäller enbart "r". I det mer komplicerade fallet
A = B + C B = A + D A = A + 1.0 B = A + 2.0 GO TO 10gäller för A "drrdr" och för B "rdd". Vi kallar dessa väguttryck (eng. path expressions). Det för A är logiskt, medan det för B är ologiskt, det erfordras naturligtvis en tilldelning före avsnittet, men allvarligare är att B beräknas (olika) två gånger, men det först beräknade används inte.
Om ett väguttryck innehåller någon av delföljderna "ur", "dd" eller "du" bör man studera programmet närmare. Det första innebär att en variabel används direkt efter att den blivit odefinierad, det andra innebär upprepade definitioner utan mellanliggande användning, och det tredje att efter att värdet har definierats så blir det odefinierat utan att någonsin ha använts. - Man kan införa konventionen att från början är alla variabler "u" om de ej har getts initialvärden genom en DATA eller PARAMETER sats.
Vi bör notera att den nämnda förenklingen av matrishanteringen är allvarlig i den meningen att den ofta förhindrar upptäckt av ett felaktigt dataflöde.
DIMENSION R(100,2) READ(5,*) (R(I,1), I = 1, 100) CALL SQUARE(R) WRITE(6,20) (R(I,2), I = 1, 100) 20 FORMAT(1X,8F10.2) STOP END SUBROUTINE SQUARE(R) DIMENSION R(100,2) DO 10 I = 1, 100 R(I,1) = R(I,2)**2 10 CONTINUE RETURN ENDHär borde tilldelningssatsen i subrutinens DO-slinga i stället varit
R(I,2) = R(I,1)**2eftersom man uppenbarligen blandat ihop de båda kolumnerna, men förenklad dataflödesanalys ger ingen ledning.
Själv tycker jag inte om interaktiva avlusningshjälpmedel. En anledning är att jag nu använder Fortran på fem olika datorsystem (DEC ULTRIX, Sun, Silicon Graphics, Cray och IBM PC), samt tidigare även på IBM 7090, IBM 360, CD 6600, DEC-20, DEC VAX/VMS och Hewlett Packard. De flesta av dessa hade helt olika system för felsökning, en del av dem har i och för sig bra system för interaktiv avlusning, men det blir helt enkelt alltför jobbigt att lära sig alla avlusningssystem.
Jag föredrar därför att göra felsökning i satsmiljö, dels utnyttjande systemens möjligheter, dels genom att lägga in egna Fortran satser i koden på de ställen där det verkar gå fel. Ett viktigt exempel på inlagda hjälpmedel är de väljare som kan utnyttjas vid kompileringen.
VAX-11 SYMBOLIC DEBUGGER är ett interaktivt hjälpmedel avsett för avlusning av program skrivna i språk under VMS. Med hjälp av avlusaren (debuggern) kan program exekveras steg för steg och brytpunkter kan sättas, exekveringsföljden kan övervakas, värden på variabler kan undersökas och modifieras. Den är ett mycket kraftfullt hjälpmedel vid uttestning av program.
TOOLPACK är en samling programmeringsverktyg från NAG avsedda för programutveckling i Fortran 77. Ett "verktyg" är ett program som kan användas vid konstruktion, analys, uttestning, förändring eller underhåll av tillämpningsprogram. Inmatning till ett sådant verktyg är användarens program, och programmet producerar en eller båda av
En del av de modernare kompilatorerna innehåller utmärkta hjälpmedel för att hitta fel. Jag har således upptäckt att NAG:s Fortran 90 kompilator kan upptäcka om två rutiner i ett användarprogram använder olika datatyp i en viss position i argumentlistan, vid anropet av en tredje rutin som befinner sig i ett programbibliotek. Eftersom den tredje rutinen är i ett programbibliotek kan inte systemet normalt avgöra vilken datatyp som är den rätta, utan den rapporterar bara att olika datatyp har utnyttjats vid de båda anropen.
Det är lämpligt att kompilera sitt program på ett relativt tidigt stadium, kanske rutin för rutin, för att få bort de rent formella felen. Dessa kan nämligen bli väldigt många vid stora helt nyskrivna program. Ett problem är att om en sats i början av en programenhet underkänns kan hela fortsättningen bli "rappakalja" för kompilatorn. Man bör därför alltid börja med att eliminera felen uppifrån. En användbar teknik är att göra en stomme för det fullständiga programmet men att till en början låta ingående subrutiner och funktioner vara "tomma", dvs bara innehålla argumentlista, deklarationer av variablerna i denna, samt återhopp (med eventuellt funktionsvärde till exempel noll).
Ett sätt att få fler fel att visa sig redan vid kompileringen är att använda IMPLICIT NONE i varje programenhet eller att ge väljaren -u vid kompileringen under många Fortran 77 system. Då signaleras alla variabler som ej deklarerats. Som tidigare nämnts är det då ofta fråga om stavfel.
Ett annat sätt att få fler fel att visa sig redan vid kompileringen är att vid Fortran 90 använda det nya begreppet avsikt eller INTENT vid deklaration av alla formella variabler i funktioner och subrutiner. Då signaleras alla fall då variabler använts på fel sätt (ur denna synpunkt). De tre alternativen är som bekant IN, INOUT och OUT. Man måste dock vara noggrann vid specifikationen, och tänka sig för noga om variabeln är in eller ut eller både och i den aktuella rutinen. Valet INOUT bör ej användas i onödan, eftersom det innebär att test-möjligheterna minskar (pga helgarderingen).
Vid in- och utmatning på filer kan ett flertal fel uppstå. Det är viktigt att skilja på formaterad och oformaterad in- och utmatning, blandning av dessa två begrepp får ej ske på samma fil. Notera att list-styrd in- och utmatning är ett specialfall av formaterad, men att den följer speciella regler vad avser antalet tecken som skall matas in, vilket gör den praktiskt oanvändbar vid inmatning av text.
Vid användning av dynamisk minnesallokering erhålles ofta fel på grund av att nödvändigt minnesutrymme ej har allokerats.
Ett vanligt fel vid användning av programbibliotek är att detta har en annan flyttalsprecision än den man använder i det egna programmet. Exempelvis kan NAG-biblioteket vara i dubbel precision, varvid användning från program i enkel precision ger helt fel resultat och ibland obegripliga felutskrifter (om man inte konverterar vid anropet).
Det gäller att ha ett antal väl utarbetade och genomräknade testfall att kontrollera programmet med. Dessutom måste man skriva programmet på ett systematiskt sätt, så att det är lätt även för en människa att förstå vad som skall ske.
Tänk på att lägga in robusthet i programmet, till exempel genom en felutskrift om något ej tillåtet värde av en viss variabel erhålles.
Tänk på att lägga in utmatning av alla indata, så att du kan kolla att de kommit in rätt. Detta ger då även en värdefull dokumentation av beräkningen.
Komplettera testfallen! Vad händer till exempel vid slumpvisa indata?
Komplettera användarhandledningen (så att programmet förhoppningsvis åtminstone användes på det sätt du avsett).
Kom ihåg att Fortran kompilatorn arbetar programenhet för programenhet, och därför normalt inte ser eventuella inkonsekvenser mellan huvudprogram, subrutin och funktion.