Felsökningsmetoder för Cortex-M3/M4 och RTOS
Det sägs att en bild säger mer än tusen ord. Mattias Norlander och Mark Moran från Atollic AB visar i denna artikel hur man kan använda grafiska informationsvyer i utvecklingsverktyg för att förenkla den besvärliga processen att felsöka mjukvara i inbyggda system.
Moderna integrerade miljöer för C/C++-utveckling, som Atollic TrueSTUDIO, ger utvecklare av inbyggda system många olika sätt att visualisera applikationens beteende även i de mest komplexa system. Trots det finns det många möjligheter som många utvecklare inte använder.
Denna artikel fokuserar på två relativt nya felsökningsteknologier; RTOS- eller kernel aware (KA)-felsökning samt Serial Wire Viewer (SWV)-realtidstracing. KA är generisk och processoroberoende medan SWV enbart kan tillämpas på vissa ARM Cortexprocessorer (t ex Cortex-M3 och Cortex-M4 som är mycket populära just nu). Vi beskriver dessa två teknologiers kompletterande natur och ser hur de på ett effektivt sätt, individuellt eller tillsammans, kan användas för att eliminera besvärliga mjukvarufel och därmed förbättra mjukvarukvaliteten.
Felsökning generellt
Att felsöka mjukvara är både komplicerat och tidskrävande. Det finns en mängd böcker om ”best practice” inom mjukvaruutveckling, däremot relativt få böcker som täcker det viktiga området felsökning av inbyggda system. Best practice-metoder inom felsökning finns till största delen lagrad som erfarenhet hos erfarna utvecklingsingenjörer med många år i branschen. Spridning av denna erfarenhet sker långsamt via artiklar, tutorials, informella diskussioner, mentorskap liksom även via en stor portion ”trial and error”. I de tidiga stegen av ett projekt när separata funktioner och kodblock utvecklas är utmaningarna runt felsökning små. Fel hittas vanligtvis via källkodsinspektion eller genom användandet av assert()-makron. Senare i projektet under integration och testning börjar uppgiften bli desto mer komplicerad. Vidare är det ofta under de senare delarna i projektet som tidpressen börjar göra sig påmind. Man har spenderat månader med att ta fram specifikationer, kravdokument, valt verktyg, hårdvara, studerat middleware, skrivit programkod och gjort preliminär testning. Vid denna tidpunkt ligger många projekt efter sitt tidsschema. Den svåraste och mest frustrerande delen i ett utvecklingsprojekt ligger oftast i att lyckas färdigställa det på utsatt tid.
Fig 1. Funktioner för kodgranskning och granskningsmöten i Atollic TrueSTUDIO.
Det bästa sättet att lyckas förkorta tidsåtgången för felsökning är genom att i största möjliga mån tillse att inga fel introduceras från början. En metodik för detta avhandlas exempelvis i James W. Grenning’s bok “Test Driven Development for Embedded C.” En annan ansats är att hålla sig strikt till kodningsstandarder samt att regelbundet utföra källkodsinspektioner. Atollic TrueSTUDIO har t ex integrerat stöd för källkodsinspektioner och kodgranskningsmöten direkt inne i IDE’n. Fig 1 visar en källkodsinspektion där en av granskarna upptäcker ett hårdkodat nummer i koden. Detta är i sig nödvändigtvis inget fel vid kodgranskningens tidpunkt, men kan däremot ställa till med problem senare under projektets utveckling och en symbolisk konstant kanske hade varit bättre om värdet senare behöver ändras.
Historiska noteringar runt felsökning med jtag
Erfarna utvecklare av inbyggda system minns att felsökning för några år sedan var synonymt med in-circuit emulatorer (ICE). Utbredd användning av denna typ av utrustning kom senare att dö ut på grund av höga kostnader samt tillförlitlighetsproblem när processorernas klockfrekvens ständigt ökade. Kvar fanns en samling felsökningstekniker som sträcker sig från att blinka med lysdioder, printf()-utskrifter, ROM-monitorer och diverse debugmode-implementationer (DBM). JTAG utvecklades från början för att användas till boundary scanning, men gjorde sedan sitt intåg som felsökningsteknologi för inbyggda system strax innan sekelskiftet. Sedan dess har JTAG vuxit dramatiskt.
JTAG som felsökningsteknologi består av tre separata delar:
1. Ett on-chip IP-block som skickar felsökningsinformation från processorn till debuggern som kör på värd-PCn.
2. En felsökningshårdvara som kopplar samman värd-PCn med målsystemet samt översätter information från on-chip IP-blocket till kommunikationskanaler (UART, USB eller Ethernet) på PCn (till exempel en JTAG-probe).
3. Ett program på PC för att skicka/ta emot information, kontrollera målsystemets hårdvara under test samt även visualisera information om målsystemet (dvs en debugger).
Traditionell felsökning med JTAG-verktyg inkluderar funktioner för att exekvera applikationen, köra applikationen till en viss kodrad, stega kod och sätta brytpunkter. Målsystemets minne kan visas och modifieras när exekveringen pausats.
Embedded trace macrocell (ETM) är en utökning av on-chip IP som möjliggör instruktions- och datatracing. ETM används typiskt i high-end-processorer. För ett par år sedan introducerade ARM Ltd, från Cambridge i England, CoreSight specifikationen för ARM Cortex-processorer. Denna specifikation utvidgar möjligheterna med traditionell JTAG-felsökning.
Fig 2. Vanliga felsökningsvyer i traditionell start/stop-felsökning.
Som tidigare nämnts fokuserar denna artikel på ARM Cortex-M3- och M4-kretsar. För att hålla Cortex-M-familjen prismässigt konkurrenskraftig, valde man att inte implementera hela CoreSight-arkitekturen i dessa processorkärnor. Detta har vissa nackdelar, men fördelarna är fortfarande flera. Utvecklaren har fått tillgång till bra felsökningsfunktioner till ett mycket fördelaktigt pris. Den felsökningsteknik som finns tillgänglig genom CoreSight på Cortex-M3/M4-kärnor är känd som Serial Wire Viewer (SWV).
Praktisk nytta med swv
Med hjälp av SWV får utvecklaren tillgång till en mängd felsökningsmöjligheter vid utveckling med ARM Cortex-M3 och -M4 processorer. Följande för och -nackdelar kan listas för SWV:
Fördelar:
* Färre processorben krävs jämfört med traditionell JTAG
* Fungerar ihop med billiga JTAG-probar
* Visar data i realtid medan applikationen exekverar i full hastighet
* Resurssnål printf()-debugging via instruction trace macrocell (ITM)-kanal
* Realtidsvisualisering av minnessadresser (variabler)
* Historik över minnesaccesser
* Statistisk profilering av applikationens exekveringstid
* Analys av interrupt- och eventbeteende
* Exekveringstidsmätning av kodsektioner
Nackdelar:
* Data skickas genom SWO, vilket är ett 1-pinnars seriellt gränssnitt. Detta medför viss begränsning av dataöverföringshastigheten
* Realtidsövervakning av minnesadresser begränsas av antalet tillgängliga komparatorer, normalt fyra stycken
För att jämföra en vanlig JTAG run/stop-debuggingmiljö med en CoreSight-kapabel debuggingmiljö, se fig 2 och 3.
Fig 2 visar en traditionell JTAG debugger med vyer för variabler, brytpunkter, minnespositioner, CPU- och periferiregister. Funktioner för grafisk visualisering i realtid under tiden koden körs i målsystemet saknas.
Fig 3 visar en SWV-debugger som visar en mängd ytterligare information, t ex:
* Grafisk realtidsplottning av variabelvärden
* Statistisk profilering visar tid spenderad i olika funktioner under körning
* printf()-utskrifter via ITM visas i PC-debuggern
* Historik och statistik över interrupts
Viktigt att tänka på är hur information visas för användaren. Användaren behöver snabbt kunna bilda sig en uppfattning om systemets status och få frågor besvarade såsom:
* Uppför sig data från sensorer i realtid enligt förväntning?
* Förändrar sig sensorvärden som förväntats vid stimulering?
* Inträffar interrupts på förväntade tidpunkter och i korrekt ordning?
* Förbrukar applikationen onödigt mycket tid i vissa funktioner eller kodsektioner?
* Om en variabel får ett felaktigt värde, vilken kodrad utförde den felaktiga variabeluppdateringen?
Denna typ av information är extremt värdefull när alla olika moduler i applikationen – exempelvis drivrutiner för periferienheter, applikationskod, RTOS, TCP/IP-stack, flash-filsystem och USB-stack sätts ihop. Problem i en modul är ofta bara ett symptom på ett underliggande problem i en annan modul även om korrelationen kan vara svår att förstå. I detta exempel ser vi rådata på ett effektivt sätt omvandlat till tolkningsbar information. En uppsjö av potentiella problem kan uteslutas redan vid en snabb blick.
Användandet av RTOS i inbyggda system och konsekvenser vid felsökning
Användningen av realtidsoperativsystem i inbyggda applikationer har en mängd för- och nackdelar. Dessa har flitigt diskuterats i olika artiklar, white papers, forum och inom en mängd utvecklingsgrupper. Under de senaste åren har de ökande prestanda/kostnad/resursförhållandena med processorer som Cortex-M3/M4, lett till ett ökat införande av RTOS i många projekt.
Fig 3. Grafisk visualisering i realtid med Serial Wire Viewer.
En gång i tiden ansågs användandet av RTOS vara ett hinder vid felsökning. I Michael Melkonians artikel “Getting by Without an RTOS” [Embedded Systems Programming Vol. 13 Nr. 10 September, 2000], säger författaren “Many in-circuit emulators and debuggers refuse to work reliably under an RTOS. You should be prepared to calm down angry engineers who are trying to hit an elusive breakpoint while the kernel code keeps rescheduling away”. Kommentaren speglade förmodligen den generella uppfattning för några sedan. Uppfattningen finns delvis kvar – användning av ett RTOS anses fortfarande av många vara ett extra lager av problem ovanpå den redan svåra uppgiften att felsöka en applikation. Nya felsökningshjälpmedel såsom JTAG och Serial Wire Viewer fungerar väl med RTOS-baserade applikationer, till skillnad mot in-circuit emulatorer från förr.
För att förstå hur användandet av ett RTOS i en applikation påverkar problembilden runt felsökning, behöver vi förstå vilken information som krävs för att kunna felsöka en RTOS-baserad applikation. Varje RTOS-baserad implementation karaktäriseras av följande:
* Nedbrytandet av applikationen i olika ”tasks” som hanteras av ett RTOS
* Användandet av ett API för att skapa, hantera och möjligtvis ta bort ”tasks”
* Användning av olika mekanismer för inter-task-kommunikation och synkronisering
* Användning av funktioner for tidshantering
Sett från detta perspektiv är problem rörande felsökning av en RTOS-applikation på en högre abstraktionsnivå. Vid traditionell felsökning vill utvecklaren oftast känna till lågnivådetaljer som värden på variabler, innehåll i ett CPU-register, bitmönster i SFR-register, hur applikationen beter sig vid exekvering av en kodrad, etc. Även om dessa funderingar fortfarande är aktuella, så krävs följande information för att felsöka en RTOS-baserad applikation:
* När exekvering är pausad, vad är olika tasks i för tillstånd?
* Vilken task körs, vilka tasks är redo och vilka tasks väntar på att få köra?
* Om tasks väntar på att få köra, vilken delad resurs väntar aktuell task på?
* Hur många synkroniseringsobjekt, som semaforer och mutexar, används och vad är deras status?
* Vilka händelser och meddelanden existerar och vad är det för status på dessa objekt?
* Vilka timers har gått ut, och vilka har fortfarande inte löpt ut?
Dessa frågor ger upphov till ett behov av bättre visualisering, särskilt stort är behovet vid felsökning och integrationstestning. Begreppet “kernel aware debugging”, det vill säga felsökning av RTOS-objekt, uppkom för cirka tio år sedan. Begreppet uppfattas numera som oumbärligt för utvecklare av denna typ av system, och anledningarna till detta är huvudsakligen följande två:
* Ovanstående information ger en översikt av systemets totala hälsostatus på en högre abstraktionsnivå, från ett RTOS-perspektiv.
* Den information som behövs är begravd i operativsystemets interna datastrukturer. Utvecklaren skall inte behöva söka upp minnesplatsen, där exempelvis en mutex eller timeout-flaggan för en timer är lagrad. Därtill används ofta ett RTOS i binärt format, vilket ytterligare gör det svårare för att inte säga omöjligt att gräva fram denna information då källkoden kanske inte ens finns tillgänglig.
Fig 4. Felsökning av en RTOS-applikation med Atollic TrueSTUDIO.
Moderna C/C++-IDEer för professionellt bruk har ofta inbyggt stöd för att visa RTOS-specifik information. I illustrationen nedan visas en uppsättning vyer för ”kernel aware debugging”. I detta fall är det ett system som använder Segger embOS RTOS som felsöks med Atollic TrueSTUDIO.
Kombinera RTOS-felsökning med SWV
Nackdelen med ”kernel aware debugging”, så som den typiskt för närvarande implementeras, är samma som med traditionell JTAG-felsökning. För att visualisera RTOS-specifik information som task status, semaforer, meddelanden och händelser, måste exekveringen pausas. För vissa produkter kan detta vara omöjligt utan att skada hårvaran, t ex vid motorstyrning. Vid testning av denna typ av system finns två potentiella lösningar på problemet.
Först och främst kan en begränsad men viktig mängd RTOS-objekt göras tillgängliga dynamiskt med hjälp av Serial Wire Viewer. Detta kräver dock att utvecklaren bekantar sig med RTOS-specifika datastrukturer vilket går emot tidigare resonemang. Men när tillräcklig nytta kan motiveras kan det fortfarande vara värt att gräva fram viktiga dataelement för dynamisk visualisering i realtid med hjälp av SWV.
Den andra lösningen involverar inköpet av en tilläggsprodukt som finns för vissa realtidsoperativsystem. Micriums uC/Probe är t ex ett verktyg som visar ”kernel aware debug”-information liksom även andra detaljer om en applikation i realtid. Detta kräver dock en kommunikationskanal, t ex en JTAG-probe eller USB-, LAN- eller UART-kanal och påverkar typsikt runtime-beteendets timing.
Tittar vi närmare på den första tekniken återfinns ett exempel på detta i fig 4. Vyn högst upp till höger visar ”Serial Wire Data Trace Timeline Graph” i Atollic TrueSTUDIO. Grafen speglar ett fält i en datastruktur som innehåller antalet meddelande i en meddelandekö inne i operativsystemets implementation. Meddelande skapas vanligtvis av en task för att kunna skicka information till andra tasks. Sänd-tasken lägger meddelandet i en meddelandekö och mottagar-tasken är typiskt pausad tills dess att meddelandet är tillgängligt i kön.
Fig 5. TCP/IP-trafiken kan visas realtid direkt i debuggern.
En granskning av denna figur visar det regelbundna mönstret av hur meddelande skickas och tas emot av tasks. Detta är givetvis bara ett exempel som bara är meningsfullt i denna applikation, och grafens mönster som sådant är ingen garanti för att applikationen uppför sig enligt specifikation. Med det sagt – antag att mönstret visualiserat i ”Serial Wire Data Trace Timeline Graph” faktiskt är det korrekta beteendet. Skulle ett problem uppstå där processen störs så är det enkelt att med ögat uppfatta att något oväntat inträffat. Kom ihåg att informationen genereras i realtid (utan någon som helst processorpåverkan) och därmed kan applikationen köras i realtid i sin vanliga miljö, det vill säga på riktig hårdvara, påverkat av äkta stimuli. En störning i meddelandeströmmen kan då enkelt avläsas grafiskt och korreleras exakt mot andra händelser. Vidare är axeln tidsgraderad varför utvecklaren har vetskap om exakt när störningen inträffade, vilket kan vara en viktig ledtråd för att komma närmare lösningen på problemet.
I en applikation som använder TCP/IP är antalet paket som skickas över tiden ofta av intresse. En protokollanalysator som WireShark används vanligtvis för att undersöka detta. Sådan information kan upplysa utvecklaren om huruvida applikationen ”fortfarande pratar/lyssnar”, eller om kommunikationen påvisar belastningsdalar/toppar. Fig 5 visar att Atollic TrueSTUDIO kan visa även sådan information via ”Serial Wire Data Trace Timeline Graph”. Med enbart en snabb blick kan man få en uppfattning om kommunikationshastigheten. Detta är ofta ett viktigt mått i TCP/IP-applikationer. SWV kan, som figur 5 visar, ofta påvisa viktig information vilket gör att utvecklaren slipper att växla mellan IDEn och ytterligare externa verktyg som WireShark. På så vis kan arbetet med felsökning, testning och datainsamling ytterligare effektiviseras.
En av målsättningarna med designen av Atollic TrueSTUDIO är minimering av antalet “extra” verktyg som utvecklaren behöver för att kunna utföra sitt jobb. Som tillägg till det tidigare omnämnda stödet för källkodsgranskning och SWV realtidstracing samt RTOS-specifik felsökningsfunktionalitet, har Atollic TrueSTUDIO även integrerat stöd för olika versionshanteringssystem och buggdatabaser, liksom integrerade tillbehörsmoduler för statisk källkodsanalys, exekveringsflödesanalys (mätning av testkvalitet) samt testautomation. Dessa funktioner kan ytterligare höja kodkvaliteten.
Sammanfattning
Traditionell start/stop felsökning har blivit kraftigt utökad i ARM Cortex-M arkitekturen i och med introduktionen av Serial Wire Viewer samt andra möjligheter som erbjuds med hjälp av CoreSight debugarkitektur. RTOS ”kernel aware” felsökningsvyer har utvecklats och mognat under det senaste årtiondet och anses numera vara oumbärliga för att få fram den information som behövs vid ”högnivå”-felsökning av RTOS-objekt. Utan att köpa speciella verktyg kan beteendet i viktiga RTOS-objekt nu även övervakas grafiskt i realtid med hjälp av SWV. Tillsammans ger RTOS ”kernel aware”-felsökning och SWV insikt i den inbyggda applikationen som ingen av de två teknikerna tidigare individuellt har kunnat bidra med.
Mattias Norlander och Mark Moran, Atollic AB
Filed under: Embedded