Verifiera multitrådade inbyggda system med programspårning
De flesta inbyggda system idag styrs av multitrådade program som körs i Linux eller ett realtidsoperativsystem (RTOS). Det finns många fördelar med en multitrådad arkitektur, men det kan också göra programvaran mer komplicerad och försvåra verifiering av systemet. Johan Kraft, grundare och CTO för Percepio AB beskriver här hur multitrådade system kan verifieras.
Bildtext: Exempel på visualisering av programspårning med Percepio Tracealyzer. Till vänster visas den detaljerade exekveringen av trådar, sorterade i prioritetsordning. Till höger visas olika översikter där avvikelser enklare kan upptäckas (klicka här för större bild).
Därför är multitrådade program annorlunda
Traditionella verifieringsmetoder som kodgranskning, statisk analys och funktionstester är nödvändiga men inte tillräckliga för att verifiera multitrådad programvara i inbyggda system, eftersom olika trådar kan interagera med varandra på sätt som inte framgår av källkoden. Till exempel kan operationer på en semafor eller mutex stoppa exekveringen av en tråd beroende på vad som händer i andra trådar. Trådarna tävlar också om processortid och kan ha tidsberoende fel (”race conditions”).
Multitrådade system kan lätt bli ett trassligt nät av beroenden mellan trådar, orsakade av såväl explicita som underförstådda beroenden och interaktioner. Beroendena påverkas av variationer i exekveringstider, något som inte syns i koden. Har man dessutom flerkärniga system (flera processorer) blir det än mer problematiskt eftersom till exempel kollisioner på minnesbussen kan påverka exekveringstiden på ett oförutsebart sätt.
Oönskade tidsvariationer är närmast omöjliga att kontrollera vid systemtest av multitrådade system; i vissa fall kan man bara skumma ytan på ett hav av möjliga exekveringsfall. Fel kan ligga latenta under ytan bara för att sedan dyka upp efter att systemet har tagits i drift. Och likaså kan tidsberoende fel vara helt omöjliga att reproducera i utvecklingsmiljön.
Pathfinder, den första Marslandaren NASA skickade ut, är ett bra exempel. Jag kan slå vad om att det systemet hade testats långt mer än de flesta inbyggnadssystem, men ändå råkade Pathfinder ut för flera fel under resan till Mars. Orsaken var ett problem med inverterade prioriteter (”priority inversion”) som fick systemet att starta om vid flera tillfällen. Detaljerna står att finna i denna uppsats, som jag verkligen kan rekommendera för läsning. Både för beskrivningen av problemet och för hur de slutligen hittade en lösning – genom programvaruspårning.
Bildtext: Exempel på inverterade prioriteter i Percepio Tracealyzer. Underst ser man variationer i svarstider för den markerade tråden.
Förbättra testbarheten hos multitrådade system
Om målet är att förbättra kvaliteten på multitrådad programvara hjälper det inte mycket att bara öka mängden tester. Man måste först lägga en grund genom att se till att programvaran över huvud taget är testbar, dvs att samma input alltid ger samma resultat på systemnivå oavsett variationer i exekveringstid och liknande. Det uppnår man genom att följa vedertagen praxis för konstruktion av multitrådade system. Det finns mycket att säga om vad som är vedertaget, men några grundläggande exempel är:
– Använd en etablerad metod för att sätta rätt prioriteter på periodiska trådar, som till exempel rate-monotonic scheduling där de trådar som exekverar med högst frekvens tilldelas den högsta prioriteten.
– Håll avbrottsrutiner så korta och deterministiska som möjligt. All kod som inte behöver exekvera på avbrottsnivå bör delegeras till reguljära trådar som exekverar med lägre prioritet.
– Trådar som exekverar länge, eller som uppvisar stora variationer i exekveringstid, bör ges så låg prioritet som möjligt.
– Se till att delade resurser bara kan manipuleras på ett säkert sätt, utan risk för race conditions, inverterade prioriteter eller oönskade låsningar. Den vanligaste rekommendationen är att man skyddar kritiska kodavsnitt med mutex-objekt som har inbyggt skydd mot inverterade prioriteter genom att alla involverade trådar tillfälligt ärver prioriteten från den med högst prioritet.
Med en stabil design, som tar hänsyn till multitrådning, kan du uppnå ett mer stabilt och deterministiskt beteende i ditt system, vilket förbättrar testtäckningen på systemnivå utan att du behöver öka mängden tester. På så sätt kan du minimera risken för kvardröjande fel i produktionskoden.
Verifiera realtidskraven
Inbyggda system har ofta mer eller mindre hårda krav på realtidsegenskaper och svarstider. Till exempel kan ett styrsystem för en motor behöva uppdatera styrsignalen var 5:e millisekund, och varje fördröjning är att betrakta som ett fel. Möjligheten att möta hårda realtidskrav påverkas inte bara av den specifika trådens exekveringstid utan också av samspelet med andra trådar och händelser i systemet. Ett avbrott eller en högprioriterad tråd som väcks vid fel tillfälle kan fördröja styrsignalen, och om en tråd använder en mutex eller annan delad resurs beror resultatet på vad som händer i andra trådar som använder samma resurs.
Att verifiera realtidskrav handlar alltså inte bara om att mäta exekveringstid för olika uppgifter, utan man behöver också identifiera de risker som följer av att trådarna är beroende av och interagerar med varandra.
Analysera multitrådad programvara
Men hur verifierar man då ett multitrådat inbyggt system? Hur vet du om designen är bra, och hur mäter du exekveringstider och variationer och verifierar det mot realtidskraven?
Lösningen är att observera systemet under exekvering med hjälp av programspårning. Percepio Tracealyzer är ett visuellt analysverktyg för programspårning som erbjuder ett stort antal funktioner. Det används ofta för felsökning i RTOS- och Linuxbaserade system men kan också vara till mycket hjälp när det gäller att analysera systemdesign och verifiera realtidskraven.
Med hjälp av programspårningen i Tracealyzer kan utvecklare:
– Samla in detaljerade data om trådars exekvering, interaktion och processortidsförbrukning över långa testkörningar, utan att behöva specialiserad hårdvara.
– Hitta anomalier i systemets beteende med hjälp av olika högnivåvyer, som till exempel CPU-last och statistikrapporter, samt klicka på avvikande datapunkter för att se detaljerade bilder av vad som hände i systemet vid just den tidpunkten.
– Analysera variationer i exekveringstid, till exempel med vyn ”Actor Instance Graph” som plottar tidsvariationer för varje tråd i systemet.
– Få en överblick över hur trådar beror av varandra, till exempel med vyn ”Communication Flow” som visualiserar hur trådarna interagerar via meddelandeköer, semaforer och andra kommunikationsobjekt.
Tracealyzer kräver ingen specialiserad hårdvara utan förlitar sig helt på effektiv instrumentering av programvaran i målsystemet. Instrumenteringen utnyttjar befintliga spårpunkter i RTOS-kärnan så det krävs inte ens någon instrumentering i applikationskoden; samtidigt är det fullt möjligt för den utvecklare som vill utvidga spårningen att lägga till Tracealyzer-instrumentering i sin egen kod.
Spårningsdata kan överföras till värddatorn på flera sätt, till exempel strömmas i realtid över en ethernet-förbindelse eller via en debug-prob som Tracealyzer stödjer.
Dr. Johan Kraft, CTO, Percepio AB
Filed under: Embedded
Länken till artikeln om Pathfinder-buggen verkar ha fallit bort. Ska vara https://www.cs.cornell.edu/courses/cs614/1999sp/papers/pathfinder.html