Mohammed Billoo, fondatore di MAB Labs , fornisce soluzioni basate su Linux embedded personalizzate per una vasta gamma di piattaforme hardware. In questa serie di post analizzerà le potenzialità del nuovo supporto per Linux offerto da Tracealyzer v4.4, utilizzando come esempio un progetto reale.
Recentemente ho lavorato su un driver Linux di tipo custom per utilizzare i dati trasmessi in streaming da un dispositivo esterno. Sebbene esistano meccanismi nativi del kernel Linux per assicurare la corretta funzionalità del driver, la valutazione delle prestazioni non è affatto un compito semplice. Percepio ha introdotto un importante aggiornamento relativo al supporto di Tracealyzer per il tracing di Linux e rilasciato una versione beta che rappresenta un’ottima opportunità per valutarne le potenzialità. Per questo motivo ho deciso di utilizzare Tracealyzer per Linux al fine di valutare le prestazioni del mio driver custom e identificare qualsiasi difetto. Scopo di questa serie di post è mostrare come utilizzare Tracealyzer per valutare le prestazioni di un driver del kernel Linux.
Tracealyzer per Linux sfrutta LTTng, un tracer & profiler open source che consente agli sviluppatori di valutare le prestazioni del kernel (driver inclusi). Esso supporta anche applicazioni nello spazio utente, anche se in questa serie ci si focalizzerà sul kernel. In definitiva, Tracealyzer esegue l’analisi sintattica (parse) dell’uscita di LTTng e fornisce visualizzazioni e statistiche dettagliate per consentire allo sviluppatore di valutare le prestazioni di kernel e driver.
Per capire esattamente cosa si sta cercando di ottenere, è importante schematizzare il principio di funzionamento del driver (Fig. 1):
Come si può vedere dalla figura 1, il nostro dispositivo esterno ha tre interfacce principali. L’interfaccia I2C ha il compito di controllare il dispositivo, l’interfaccia SPI è utilizzata per lo streaming dei dati verso il dispositivo Linux mentre il GPIO è una linea di interrupt che viene impiegata per indicare il momento in cui i dati sono pronti per essere utilizzati. Quando la linea del GPIO è attivata dal dispositivo, il driver Linux invierà un comando su I2C per istruire il dispositivo di avviare lo streaming, che verrà eseguito sfruttando l’interfaccia SPI. Il driver fornirà istruzioni al controllore DMA (DMAC) del sistema Linux embedded per gestire il trasferimento dei dati tra il bus SPI e la RAM di sistema al fine di assicurare che la CPU sia in grado di gestire altri processi (task). Mentre il driver fornirà semplicemente istruzioni al DMAC necessarie per eseguire i necessari trasferimenti, dovrà effettuare verifiche su base periodica per accertarsi che non sorgano problemi. Infine, il dispositivo Linux contiene il codice dell’applicazione per recuperare i dati in streaming dalla RAM e archiviarli nella memoria non volatile.
L’utilizzo di Tracealyzer consentirà di validare due metriche importanti. In primo luogo, che il tempo intercorso tra il momento in cui viene attivato il GPIO e quello in cui viene inviato il comando I2C sia ridotto al minimo. In secondo luogo, che il kernel Linux fornisca al driver un numero sufficiente di cicli di esecuzione per consentirgli di gestire periodicamente qualsiasi problema possa sorgere a livello di DMAC. L’obiettivo finale è garantire che nel processo di streaming il numero di dati persi sia minimo e Tracealyzer è il tool che permetterà di misurare questa perdita.
Configurazione del kit di sviluppo del dispositivo
Come menzionato in precedenza, Tracealyzer sfrutta i file generati da LTTng. Prima di iniziare con qualsiasi implementazione, è necessario configurare in maniera appropriata la piattaforma Linux embedded in modo che possa supportare LTTng. La piattaforma selezionata è il kit di sviluppo Phytec i.MX 6ULL. In passato avevo utilizzato altri kit di sviluppo di Phytec e mi sono trovato a mio agio con i loro BSP (Board Support Package) basati su Yocto.
Per supportare LTTng è necessario personalizzare il BSP, poiché il BSP standard per questa scheda non include di default LTTng. Per far ciò è necessario creare un nostro strato (layer) al di sopra dello strato “poky” (che contiene i tool e i metadati fondamentali di Yocto) e di quello sviluppato da Phytec.
Sebbene una discussione più approfondita sul progetto Yocto esuli dagli scopi del presente articolo, è possibile seguirlo utilizzando il mio github repo.
Il nostro strato “custom” è denominato “meta-mab-percepio” e la struttura della sua directory è la seguente:
tree -d sources/meta-mab-percepio/
sources/meta-mab-percepio/
├── conf
├── recipes-images
├── images
└── packagegroups
└── recipes-kernel
└── linux
└── linux-mainline
- conf si riferisce alla configurazione dello strato, secondo la prassi di Yocto.
- recipes-images/packagegroups contiene “packagegroup-custom.bb”, un file che include i package LTTng necessari di cui avremo bisogno nelle immagini risultanti.
- recipes-images/images/contiene “phytec-headless-image.bbappend”, che è la nostra personalizzazione dell’immagine Linux di Phytec. Esso aggiunge semplicemente “packagegroup-custom” all’immagine.
- recipes-kernel/linux/contiene una ricetta (recipe, ovvero un tipo di metadato contenente le istruzioni per creare i package finali) per aggiungere la personalizzazione al kernel Linux richiesta per supportare LTTng.
Grazie alle personalizzazioni appena descritte, saremo in grado di generare l’immagine di Linux, caricarla sulla scheda SD, effettuare il boot sul kit di sviluppo e farla girare con le istruzioni disponibili sulla pagina Web “Getting Started with Tracealyzer for Linux” di Percepio per acquisire il trace.
Dopo aver eseguito i comandi necessari descritti nel link appena sopra menzionato e ottenuta una directory denominata “lttng-traces” nella directory home della directory principale (root), è possibile copiarla sulla nostra macchina x86 (_64) per l’analisi con Tracealyzer. Ciò può essere fatto semplicemente disalimentando il kit di sviluppo Phytec e copiando la directory dalla scheda SD al PC. E’ anche possibile trasferire i dati di trace sfruttando una connessione di rete, in quanto Tracealyzer supporta tale modalità.
Le visualizzazioni più importanti in questa fase
Una volta lanciato Tracealyzer, è necessario selezionare “File->Open Folder” e la directory “lttng-traces”. Tracealyzer analizzerà la directory relativa alla sessione di trace acquisita e caricherà tutti i file inclusi (un trace LTTng risulta composto da più file). A questo punto possiamo scegliere la visualizzazione più appropriata per analizzare le prestazioni del nostro driver. Anche se Tracealyzer mette a disposizione visualizzazioni in grado di fornire informazioni utili e dettagliate relative all’interazione tra kernel e spazio dell’utente, la nostra attenzione sarà focalizzata sullo spazio del kernel, e quindi sulle visualizzazioni “Trace View”, “Actor Instances”, “CPU Load Graph” e “Context Switch Intensity”. Queste visualizzazioni possono essere selezionate cliccando sulle relative icone disposte sulla barra a sinistra della schermata principale di Tracealyzer (Fig. 2).
Fig. 2
Aprendo la visualizzazione “Trace View, comparirà il grafico riportato in figura 3:
Fig. 3
La colonna più a sinistra riporta gli “Actor Names”, che sono i processi/thread del kernel e dello spazio utente che sono in esecuzione in un determinato momento. La colonna più a sinistra (in grigio) è il processo di “swapper” del kernel, ovvero il processo di “idle” nel kernel Linux. Spostandoci verso destra si nota una linea blu, che è un thread del kernel (“kworker”). Nel momento in cui implementiamo il driver e monitoriamo le sue prestazioni mediante Tracealyzer, utilizzeremo “Trace View” per verificare che al nostro thread del kernel vengano assegnate risorse di esecuzione sufficienti per gestire il trasferimento DMA. Spostandoci ulteriormente verso destra vedremo il processo dello spazio utente “lttng” in verde e infine il processo dello spazio utente “rcu_sched” in giallo. Un’analisi dettagliata di “rcu_sched” esula dagli scopi di questa trattazione, ma questo articolo rappresenta un ottimo punto di partenza.
A questo punto, cliccando con il pulsante destro del mouse nella finestra e selezionando “Zoom Out – Show Full Traces”, verrà mostrata la visualizzazione concisa di tutti i thread e i processi eseguiti durante l’intera durata dell’acquisizione (Fig. 4).
Fig. 4
Dall’esame di figura 4 è possibile osservare che lo swapper sta utilizzando la maggior parte delle risorse di esecuzione nel corso dell’intera acquisizione. Ciò appare logico, in quanto nessuno processo significativo è attualmente in esecuzione. Quando il nostro driver è in esecuzione e sta trasferendo i dati dal nostro dispositivo alla scheda di sviluppo Phytec, ci aspetteremo di vedere sezioni di “Trace View” che sono marcate da frammenti colorati in blu. Ciò starebbe a indicare che al nostro specifico thread del kernel verrà assegnata una quota maggiore delle risorse di esecuzione disponibili sulla piattaforma per effettuare il trasferimento.
L’altra visualizzazione che assume una grande importanza è “Actor Instances”, dove abbiamo selezionato l’”Execution Time” nella casella a discesa ubicata in alto a sinistra. (Fig. 5).
Fig. 5
L’asse y di questo grafico indica il periodo di tempo occupata da un particolare attore (actor – ovvero ogni elemento in esecuzione, come un thread o un processo). Una volta selezionato uno dei thread del kernel, è possibile osservare la presenza di alcuni “spike” (picchi) relativamente al suo tempo di esecuzione. E’ inoltre possibile rilevare che questi spike hanno una durata approssimativa di 350, 450 e 550 microsecondi. Per comprendere esattamente se questi picchi possano essere fonte di potenziali problemi, bisognerebbe conoscere i requisiti di temporizzazione del nostro sistema, oppure valutare le modalità di funzionamento del sistema in condizioni “normali”. Poiché non sussistono requisiti particolari, è necessario cercare di capire se questi spike possano rappresentare un reale problema. Per far ciò si farà ricorso alla prossima visualizzazione. Se selezioniamo un altro thread del kernel, vedremo il grafico anomalo riportato in figura 6.
Fig. 6
Questo grafico evidenzia la presenza di un elevato picco nel tempo di esecuzione di uno dei thread del kernel. Ancora una volta, utilizzeremo la visualizzazione successiva per verificare se ciò può rappresentare motivo di preoccupazione.
Come precedentemente accennato, la visualizzazione successiva è “CPU load”, che riporta il tasso di utilizzo della CPU da parte dei differenti attori. Una volta aperta questa visualizzazione, apparirà il grafico riportato in figura 7.
Fig. 7
Da questo grafico si evince che non sussiste alcun motivo di preoccupazione. Il tasso di utilizzo della CPU per entrambi i thread del kernel non supera mai la percentuale dell’1,1%, per cui ciò che è riportato nel grafico relativo all’”Actor Instance” era semplicemente un picco relativo al tempo di esecuzione del thread del kernel verde rispetto al thread del kernel blu. E’ importante notare che Tracealyzer imposta per default l’asse y per garantire la migliore visualizzazione dei dati: questo è il motivo per cui non vediamo l’asse y limitato al 100%. Nel caso selezionassimo l’attore “lttng-sessiond” nel grafico “CPU load” vedremmo il grafico di figura 8:
Fig. 8
Da qui si può osservare che il processo “lttng-sessiond” occupa il 90% del tempo di CPU in prossimità dell’inizio del trace (ancora una volta è importante sottolineare il fatto che Tracealyzer regola l’asse y in modo da fornire una visualizzazione significativa dei dati). Anche se ciò è significativo, si tratta di un fatto prevedibile poichè il daemon (processo in background) dello spazio utente “lttng” deve effettuare l’inizializzazione necessaria per l’acquisizione delle tracce. Oltre a ciò, non vi è molto altro a contendersi il tempo di CPU. A scopo di confronto, potremmo anche vedere l’utilizzo della CPU da parte dei thread del kernel.
La visualizzazione finale, “Context Switch Intensity”, ci consente di validare il funzionamento del nostro driver del kernel, garantendo nel contempo che non provochi il fenomeni di thrashing (in pratica di “ingolfamento”) del kernel.
Fig. 9
Osservando la visualizzazione di figura 9, si può notare che non vi sono commutazioni di contesto (context switch) significative per un particolare thread del kernel rispetto ad altri. Se vi fosse un problema di prestazioni per il nostro driver, si osserverebbero commutazioni di contesto significative del nostro thread del kernel. Ciò sarebbe imputabile allo scheduler del kernel, che assegnerà il tempo di esecuzione al nostro thread del kernel, quindi dopo un certo periodo di tempo si sposterà su un altro thread, ma poi commuterà immediatamente sul nostro thread nel caso quest’ultimo abbia richiesto risorse di esecuzione. Ancora una volta, la valutazione dell’accettabilità o meno di circa 20 commutazioni di contesto (si faccia riferimento sempre alla figura 9), dipenderà dai requisiti di sistema o dalle misure effettuate quando il sistema si comporta “in modo normale”:
Queste visualizzazioni rappresentano un valido mezzo per ottenere in tempi brevi una panoramica del trace e localizzare i “punti caldi” o le anomalie di interesse da sottoporre a ulteriori analisi, soprattutto nel caso in cui non si sappia esattamente che cosa cercare. Questo è senza dubbio uno dei principali vantaggi di Tracealyzer, in quanto una ricerca di questo tipo potrebbe risultare molto difficoltosa in presenza di tracce molto lunghe con parecchi migliaia (se non addirittura milioni) di eventi.
In definitiva Tracealyzer fornisce numerose e utili visualizzazioni relative all’esecuzione del nostro driver. Ciascuna di queste fornisce informazioni dettagliate uniche inerenti il sistema Linux, kernel incluso. Una volta abbinate, tali visualizzazioni dovrebbero essere utilizzate per fornirci una visione olistica del nostro driver che ci permetta di garantire l’assenza di “colli di bottiglia” in termini di prestazioni o di identificare la causa di qualsiasi problema e condurre quindi le analisi del caso. Nel prosieguo, inizieremo a realizzare il nostro driver e continueremo a utilizzare Tracealyzer per validare le sue prestazioni.
Informazione sull’autore
E’ professore aggiunto di Ingegneria Elettrica presso la “Cooper Union for the Advancement of Science and Art” dove insegna ai corsi di Logica digitale, Progettazione e Architetture di Computer.
Mohammed ha conseguito la laurea e il successivo master in Ingegneria Elettrica presso la stessa istituzione.
Questo è il primo di una serie di articoli focalizzati sull’uso di Tracealyzer per acquisire e analizzare la diagnostica del trace di tipo visuale per sistemi Linux embedded.