Scuola Politecnica e delle Scienze di Base
Corso di Laurea in Ingegneria Informatica
Elaborato finale in Sistemi Real-Time
Analisi di soluzioni per il real-time
monitoring
Anno Accademico 2013-2014
Candidato:
Andrea Aletto
matr. N46000969
Indice
Indice ................................................................................................................................................... II
Introduzione ......................................................................................................................................... 3
Capitolo 1: “Run-Time Monitoring of Real-Time Systems” ............................................................... 6
1.1 Eventi..................................................................................................................................... 7
1.2 Tipo di monitoraggio ............................................................................................................. 9
1.2.1
Monitoraggio attivo........................................................................................................ 9
1.2.2
Monitoraggio passivo ................................................................................................... 11
1.3 Satisfiability Checker .......................................................................................................... 13
Capitolo 2: Momentics per QNX-Neutrino ....................................................................................... 17
2.1 System Information ................................................................................................................. 18
2.2 SAT ......................................................................................................................................... 21
2.3 QNX System Profiler .............................................................................................................. 24
Capitolo 3: Monitoring per VxWorks ................................................................................................ 26
3.1 Monitoring per Workbench ................................................................................................. 28
3.1.1
CoverageScope............................................................................................................. 28
3.1.2
MemScope ................................................................................................................... 29
3.2 Monitoring per Tornado ...................................................................................................... 29
3.2.1
WindView .................................................................................................................... 30
3.2.2
Spy ............................................................................................................................... 31
3.3 Tracealyzer .......................................................................................................................... 32
Conclusioni ........................................................................................................................................ 34
Bibliografia ........................................................................................................................................ 36
Introduzione
Nella società odierna ricoprono un ruolo di primaria importanza i sistemi in tempo reale,
che fondano la validità del calcolo effettuato sul concetto di “esattezza logica” (il sistema
produce il risultato esatto) e di “correttezza temporale” (il risultato è ottenuto in tempo).
Sebbene i sistemi real-time trovino utilizzo in un vastissimo campo applicativo, un fattore
comune sempre presente e determinante è insito nel concetto di “deadline”, la cui
caratterizzazione determina appieno la criticità del sistema: si parla infatti di sistemi soft,
firm e hard real-time a seconda del “danno” provocato dallo sforamento di una deadline.
Sarebbe, ad esempio, inaccettabile il ritardo di una segnalazione su un radar in ambito
avionico, per cui è obbligatorio investire le dovute risorse per garantire sempre il corretto
funzionamento in tempo reale dell’intero sistema; è, viceversa, accettabile uno sforamento
di deadline in ambito telefonico, in quanto ciò comporterebbe esclusivamente un
degradamento della Quality of Service (QoS) fornita dal sistema. È, inoltre, possibile
notare che un qualunque sistema multi-task è composto da almeno un processo il cui
fallimento può portare in uno stato inconsistente l’intero apparato: tali processi vengono
definiti “task hard RT”; a questi si contrappongono naturalmente i “task soft RT”, il cui
fallimento porta, ancora una volta, soltanto un degradamento della QoS.
Spesso, sbagliando, si associa il concetto di “tempo reale” con quello di “velocità di
esecuzione” in termini di velocità media di completamento dei task. Risiede in questo
un’altra fondamentale differenza con i sistemi non real-time: non è importante che il
3
sistema sia nel complesso veloce e reattivo, ma soltanto che venga sforato il minor numero
possibile di deadline (talvolta, un solo requisito temporale non rispettato può portare a
conseguenze catastrofiche). È dunque di fondamentale importanza, fin dalla fase di
sviluppo del software, la predicibilità dello stesso, chiedendosi, di conseguenza, quali
siano le fonti di non-determinismo che minano una corretta analisi statica, trovando
risposta in un insieme pressoché infinito di cause, che vanno dall’incertezza strumentale
nei dispositivi (periferiche di I/O) interfacciati al sistema da testare, fino a problemi interni
allo stesso sistema operativo. Essendo impossibile modellare la realtà con cui dover
interagire, risulta d’obbligo analizzare le cause di non determinismo interne e provare, per
quanto possibile, a predirle. È chiaro quindi che un sistema operativo time-sharing,
costruito per garantire il massimo grado di parallelismo nell’esperienza dell’utente, che
nella quasi totalità dei casi è umano, non può in nessun modo soddisfare le stringenti
richieste temporali di un sistema in tempo reale: è necessaria l’introduzione di un kernel
specifico, che supporti system-calls che tengano conto dei task RT e che implementi delle
apposite strutture dati per garantire i vincoli di priorità nella schedulazione del task.
Un sistema operativo pensato e realizzato per l’ambito del tempo reale è chiamato RTOS
(Real-Time Operating System) e attualmente sul mercato ne esistono più di un centinaio
tra kernel-RT commerciali e open-source, adattati per diverse piattaforme, dai pochi KB
per le rom dei MCU di “FreeRTOS”, a dimensioni ben più sostenute di “VxWorks” o
“QNX”.
La quasi totalità di questi kernel fornisce anche dei meccanismi di scheduling basati di
solito su priorità fisse con la possibilità di prelazione, dal momento che essa permette
latenze più basse e una efficienza di risorse maggiore. Tipicamente, gli RTOS sono
accompagnati da una suite software che implementa diverse funzionalità utili per lo
sviluppo e per l’analisi di applicazioni in tempo reale come ad esempio il “tracer”, per
visualizzare le attività del kernel con un’interfaccia tipicamente user-friendly, il
“performance profiler”, per misurare le prestazioni del sistema, strumenti di monitoraggio
per conoscere lo stato dei task e delle relative variabili a run-time.
Essendoci, quindi, una criticità così forte sul concetto di tempo, sorge la necessità di poter
4
monitorare i processi attivi sul sistema, tramite un’analisi che, sia essa manuale o
automatica, permetta di capire se il sistema “does the right thing on time”.
Verranno nel seguito considerate delle strategie per riuscire nell’obiettivo di monitorare il
sistema, in particolare verrà presentata dapprima una soluzione teorica che fonda le sue
basi in uno studio universitario americano, il quale fa delle ipotesi sulla struttura che un
task RT deve avere per poter agevolare l’azione di monitoraggio; nel seguito di tale
elaborato, invece, verranno mostrate delle soluzioni software (sia proprietario sia open
source) per il monitoraggio specifico dei sistemi operativi real-time che dominano lo
scenario commerciale dei sistemi embedded, ovvero QNX e VxWorks.
5
Capitolo 1: “Run-Time Monitoring of Real-Time Systems”
Una prima soluzione analizzata è lo studio teorico, poi implementato al calcolatore,
battezzato col nome di “Run-Time Monitoring of Real-Time Systems”, dei ricercatori
Chodrow (University of Texas at Austin, TX), Jahanian e Donner (IBM Research Center,
NY), la cui idea è stata quella di fare in modo che il sistema potesse in qualche modo
“auto-monitorarsi”, in modo da avere un feedback affidabile dello stato corrente del
sistema e potersi quindi autogestire, risolvendo bloccaggi, deadlock e ritardi in maniera
estremamente dinamica e contestualizzata. L’insieme dei task del sistema è diviso in 3
parti: “tasks with embedded constraints”, “watchable tasks” e un unico processo
osservatore, denominato per questo “monitor”.
L’intero studio si basa sulla possibilità da parte dei task dei poter “segnalare” degli eventi,
che chiaramente saranno diversificati a seconda del campo applicativo del modello, però è
comunque possibile distinguere degli “eventi fondamentali”, i quali sono sempre presenti
in ogni tipo di applicazione, come ad esempio l’inizio o la fine di una procedura, la
segnalazione di lock e unlock di una risorsa protetta, la lettura e la scrittura di un
messaggio in una coda IPC, ecc. Tali eventi verranno registrati in un segmento di
memoria. Una parte del sistema si occupa di tradurre le specifiche temporali dei task in
vincoli logici e, dall’analisi dei timestamp delle occorrenze del singolo evento, è possibile
capire se il task sta funzionando nel modo corretto.
Secondo la pubblicazione, il primo tipo di processo deve essere un vero e proprio task
6
auto-monitorante, nel senso che contiene, al suo interno, strutture dati e istruzioni tali da
riuscire a implementare
degli statement di verifica sul soddisfacimento dei vincoli
temporali; il secondo, invece, deve essere un processo che, seppur a conoscenza
dell’azione di monitoraggio, deve lasciare tale procedura ad un altro sistema esterno,
concepito esclusivamente per eseguire questo compito.
1.1 Eventi
Lo progressione di un task RT si può vedere come una sequenza di occorrenze di eventi;
in maniera informale si definisce come “evento” una serie di “cose che accadono”[2] in un
sistema e l’occorrenza di esso fissa l’istante di tempo ben preciso in cui si verifica lo
stesso. Gli eventi distinguibili sono di due categorie: la prima, gli eventi “label”, sono
quelli che definiscono l’inizio e la fine di una certa sequenza di istruzioni (una funzione,
un task, un ciclo, ecc.) e vengono formalmente indicati con una freccia che punta verso
l’alto (inizio) o verso il basso (fine) posta affianco al nome scelto per l’evento, e.g. “E1↓”
rappresenta la fine dello statement chiamato E1. A questi si contrappongono gli eventi
“transitori” che catturano il valore di una determinata variabile (detta “watchable
variable”) dedita allo storage di informazioni riguardante lo stato del sistema e la sua
integrità in termini di tempo reale. Un sistema real-time evolve mentre accadono questi
due tipi di eventi, è dunque necessario esaminarli, tenerne traccia e trarre da essi
informazioni; inoltre gli algoritmi per salvare le occorrenze di un evento sono molto
semplici (si tratta solo di catturare un timestamp, salvare il numero delle occorrenze e dare
un nome a questo record) e le informazioni ricavabili sono estremamente significative, per
cui è ragionevole pensare di dedicarci un segmento di memoria.
È verosimile uno scenario in cui, durante un’esecuzione concorrente di più task RT,
l’occorrenza di un certo evento generico sia segnalata da più di un task e si sfrutti questo
dato per effettuare delle operazioni logiche: ad esempio si definisca l’evento E1 “è stata
completata una transazione in un tempo t inferiore a 1ms”, l’evento E2 “non completare
più di 10 transazioni entro un 1ms” e l’evento E3 “inviare una segnalazione se l’ultima
7
transazione effettuata risale a più di 1s fa”; questi tre eventi sono generici e non fanno
esplicitamente riferimento a un ben determinato task, per cui tutti i Pi processi attivi (che
supponiamo concorrenti) potrebbero invocare un’occorrenza degli eventi. È, quindi,
necessario poter tenere traccia non solo dell’ultima occorrenza, ma di uno “registro
storico” dell’evento, che contiene sia il numero totale
delle occorrenze chiamate, sia il timestamp della più
recente.
Viene quindi utilizzata la seguente struttura per l’evento,
contenente un identificativo univoco, un counter di quante
occorrenze si sono verificate in totale, una coda circolare
che conserva timestamp e valore dell’eventuale variabile,
un indice relativo che costituisce un puntatore alla testa
della coda circolare ed infine, siccome la scelta della
Figura 1.1: Record evento
dimensione dello storico dell’evento è lasciata al progettista, un puntatore alla fine della
struct, come in figura 1.1.
Parallelamente al formalismo teorico, è affiancata una proposta di implementazione,
utilizzando funzioni RTL-like per l’accesso sicuro, sequenziale ed esclusivo al record che
caratterizza l’evento. Funzioni di esempio che vengono proposte sono:
• “@(e,i)” dove e=id_evento, i=index. Restituisce il timestamp della i-esima
occorrenza dell’evento richiesto, facendo la convenzione che le occorrenze
vengano ordinate per timestamp crescenti. È possibile chiamare la funzione con un
indice negativo, avendo come risultato la i-esima occorrenza più recente, ovvero
una ricerca nello storico a partire dalla fine della tabella.
• “@val(v,i)” dove v=nome_watchable_variable, i=index. Restituisce il timestamp
della i-esima occorrenza della variabile v. Anche in questo caso si scorre la lista al
contrario se viene richiesto un indice negativo.
• “@index(e,i)” dove e=id_evento, i=index. Restituisce la posizione nella coda
circolare dell’occorrenza più recente dell’evento considerato.
8
1.2 Tipo di monitoraggio
Come già anticipato, i task si dividono in due gruppi: i “tasks with embedded constraints”
che implementano al loro interno meccanismi di monitoraggio e i “watchable tasks” che
vengono osservati dall’azione di un processo esterno detto monitor. Verranno di seguito
analizzate due diverse azioni di monitoraggio: il monitoring attivo, quando il task è
strutturato in modo tale da auto monitorarsi e darsi autonomamente un feedback per capire
che azione di controllo intraprendere e il monitoring passivo, quando il task dichiara i suoi
vincoli temporali, ma lascia ad un altro processo (o talvolta un altro sistema) il compito di
essere monitorato mediante appositi comandi di enable/disable.
1.2.1 Monitoraggio attivo
Una struttura di questo tipo implica necessariamente che sia il programmatore a prevedere
che il sistema debba essere monitorato e a gestire le diverse situazioni di soddisfacimento
e violazione delle specifiche real-time del processo, pertanto è una soluzione che, se da un
lato non è trasparente alle funzionalità del task, dall’altro dà garanzie maggiori in termini
di performance e affidabilità dell’azione di monitoraggio, rispetto ad un processo ignaro
dell’ambiente in cui si trova (si rimanda ai cenni conclusivi per l’analisi dei vantaggi e
svantaggi di questa scelta). Questa soluzione è chiamata anche “monitoraggio sincrono”,
poiché è presente all’interno del sorgente dell’applicazione una serie di istruzioni dedicata
esclusivamente al testing a runtime del programma, che pertanto verrà effettuato
ciclicamente ogniqualvolta il flusso di esecuzione attraversa quel blocco di statement. Ad
esempio, si consideri questo particolare vincolo, legato al corrente flusso temporale, per la
gestione di una caldaia: si vuole regolare la resistenza di una caldaia se la temperatura
dell’acqua ivi presente è salita o no troppo velocemente. Se si vuole utilizzare una
soluzione di tipo embedded il feedback necessario all’impianto (e quindi l’azione di
monitoraggio attivo) sarà del tipo
9
Figura 1.2: Esempio gestione caldaia
Questo “snippet” di codice deve essere incluso dal programmatore stesso all’interno del
codice sorgente dell’applicazione real-time.
Per quanto riguarda la struttura che il sistema dovrà avere, ci saranno un numero P di task
differenti (intesi, qualora desiderato e supportato dal kernel, anche come thread
appartenenti allo stesso processo) che devono poter accedere ad una memoria comune in
cui risiedono i vari record con gli storici degli eventi. Poiché la memoria condivisa può
essere causa di inconsistenza logica, se non correttamente usata, è necessario accedervi
utilizzando il paradigma semaforico. A tale scopo viene instanziato un task di
inizializzazione del sistema che si occupa appunto di svolgere, prima dello start delle
attività che devono eseguire sul sistema, le attività preliminari di riservare lo spazio di
memoria condivisa, inizializzare un semaforo per accedervi in maniera mutuamente
esclusiva, far partire il sistema in maniere definitiva.
Essendo una risorsa protetta essa può generare il ben noto problema dei deadlock, che
affligge la maggioranza dei sistemi operativi (anche non real-time) che svolgono attività
concorrenti e utilizzano risorse protette (tutti i sistemi
operativi time-sharing dei comuni pc da ufficio ne
costituiscono un esempio) obbligando i relativi processi ad
aspettare indefinitamente per avere il controllo assoluto di
una risorsa protetta. La soluzione a questo problema sta
nell’utilizzare un’unica risorsa condivisa (la shared
memory degli eventi per l’appunto), oppure nell’eliminare
totalmente ogni forma di concorrenza e multitasking nel
sistema; entrambi sono scenari piuttosto inverosimili, se
non in applicazioni mission-critical, ma gli studiosi di cui
10
Figura 1.3: Schema fondamentale del
monitoraggio attivo
sopra, seppur citando il problema, focalizzandosi sull’aspetto del monitoring piuttosto che
quello della corretta gestione della concorrenza in un sistema operativo, non ne offrono
una soluzione.
In definitiva, uno schema complessivo del sistema è un insieme di task, tutti collegati
all’elemento centrale che è la shared memory, come desumibile dalla figura 1.3.
1.2.2 Monitoraggio passivo
In questa architettura, il task chiede di essere verificato tramite una ben precisa richiesta di
monitoring fatta ad un task esterno (talvolta esterno non solo al processo richiedente, ma
addirittura all’intero sistema). Il processo, in questo modo, non deve implementare nel
proprio codice le funzioni e la relativa logica per capire se sta violando alcune delle
proprie specifiche, perché questa attività è svolta da un’entità concepita proprio per questo
motivo; deve tuttavia essere a conoscenza dell’esistenza del monitor, perché è il task
stesso a richiedere l’attivazione dell’azione di monitoraggio, pertanto anche in questo
caso, come nel precedente, non si ha un grado di accoppiamento nullo, però, di contro, il
processo è “alleggerito”, perché può essere ottimizzato per le funzionalità che deve
svolgere. La conoscenza del monitor, d’altra parte, è d’obbligo anche per una seconda
motivazione, che risiede nel riuso delle informazioni catalogate sugli eventi: mentre nel
monitoring attivo il task va a leggere tali informazioni autonomamente e in base ad esse
regolava il proprio comportamento, stavolta il monitor conosce i vincoli del processo, si fa
carico di controllarli ed eventualmente segnalare errori al task monitorato. Da questo si
capisce facilmente che non si ha più la necessità di dedicare un area di memoria comune ai
task, perché questi dovranno sempre e soltanto riferirsi al monitor. Dunque il processo
monitor è composto da una struttura esterna che da un lato si comporta da interfaccia per i
processi esterni e dall’altro protegge automaticamente, senza l’uso di ipc, le informazioni
vitali per il funzionamento dell’intero meccanismo analizzato, le quali saranno
naturalmente posizionate in un area di memoria allocata al monitor, visto che in ogni caso
è esso stesso l’unica entità che può e deve accedere a tali informazioni.
11
Il meccanismo del feedback è realizzato sempre a mezzo del processo esterno, il quale si
fa carico, nel caso in cui i vincoli siano violati, di segnalare il malfunzionamento al task
monitorato; la successiva gestione di tale segnalazione è data al programmatore. Il
paradigma di esecuzione-segnalazione-gestione ricorda il meccanismo delle interruzioni
nella programmazione, le quali, essendo notoriamente eventi asincroni, danno
l’appellativo di “asincrono” come sinonimo di “passivo” in questo tipo di monitoraggio.
Se si utilizza una soluzione di questo tipo, c’è però un meccanismo da dover definire per la
gestione concorrente dei salvataggi dei record degli eventi: il monitor può comunicare
sequenzialmente ed esclusivamente con un solo processo alla volta, è pertanto necessario
l’utilizzo di una coda di messaggi in cui il singolo task deposita le informazioni
riguardanti la singola occorrenza dell’ evento, informazioni che verranno poi processate
dal monitor stesso. Si evita, per ragioni di indeterminazioni e bloccaggio, di utilizzare
un’unica coda che discrimina i processi tramite un identificativo, ma si preferisce usare la
suddetta struttura di Inter Process Communication singolarmente per ogni task. La politica
di processing delle singole code è chiaramente FCFS, perché l’ordine in cui i messaggi
sono depositati è cronologico, inoltre anche la scelta di quale coda schedulare tra tutte è di
tipo FCFS, per garantire in ogni istante l’integrità temporale e cronologica dello storico
dell’evento. Entrambe le scelte (della coda e del messaggio da “consumare”) sono
fisicamente realizzate tramite un confronto del timestamp dell’occorrenza, la quale è in
questo contesto rappresentata proprio da un messaggio; il timestamp, quindi, assume un
duplice fondamentale ruolo, poiché se da un lato è utilizzato per capire se un task sta
sforando le proprie deadline, dall’altro è usato anche dallo
stesso monitor per autogestirsi e fare in modo che il
contenuto dei record degli eventi sia in ogni istante
cronologicamente coerente con lo scorrere continuo del
flusso temporale. In figura 1.4 è mostrato uno schema
semplificativo della comunicazione tra i task del sistema e
Figura 1.4: Schema fondamentale
monitoraggio passivo
il processo/sistema monitor.
Un’ultima importante questione da modellare è la maniera in cui il monitor viene a
12
conoscenza dei vincoli temporali dei task che esso analizza.
Il task deve contenere un blocco nel proprio codice in cui dichiara i propri vincoli
temporali tramite delle formule logiche (come la condizione dell’if nel precedente
paragrafo), le quali verranno lette dal monitor e convertite in un linguaggio ad esso
compatibile, ottimizzate per essere indicizzate e controllate da una componente separata
del monitor detta “Satisfiability Checker”, analizzata nel seguito.
Riassumendo, il monitoring passivo o asincrono avviene nel
modo seguente: il task chiede al monitor di essere
controllato, quest ultimo legge e converte i vincoli temporali
del task e inizia l’analisi sfruttando le regole e gli algoritmi
di controllo del Satisfiability Checker; talora i vincoli siano
stati violati, sarà immediatamente mandata una notifica al
task, il quale dovrà implementare un handler per gestire tale
segnalazione. Parallelamente al lavoro di controllo del
monitor, dovrà essere svolto il lavoro di raccolta dei dati
sulle occorrenze degli eventi che accadono nell’ambiente
Figura 1.5: Interazione
Monitor-Task
circostante al sistema.
1.3 Satisfiability Checker
È il cuore della logica di controllo del monitor, per questo motivo si è scelto di dedicare ad
esso una sezione intera, analizzando nel dettaglio il modo di conversione suggerito e
l’algoritmo di checking dei requisiti dei task richiedenti, tramite un esempio esaustivo,
oltre che presentando le regole formali su cui si fonda la componente software in esame.
Come anticipato, i vincoli vengono espressi tramite funzioni RTL-like, che è un
meccanismo semplice, ma di difficile controllo perché richiede numerose chiamate
innestate a funzioni e comparazioni, in quanto si tratta di funzioni che per loro natura sono
13
estremamente basilari e lavorano ad un livello di astrazione piuttosto basso. Ad esempio,
si consideri il seguente vincolo temporale: “l’evento RESPONSE, caratterizzato da un
inizio e una fine, deve essere completato prima di una certa deadline, indicata
dall’occorrenza dell’evento SIGNAL”; lo snippet che identifica tale vincolo è
Figura 1.6: Requisito in forma RTL-like
che utilizza 9 chiamate a funzione, 3 confronti e una AND logica. Per vincoli più
complessi, in cui compaiono anche delle OR e dei confronti condizionali, la complessità
del calcolo del vincolo a run-time cresce, introducendo ritardi sempre maggiori. Si
dimostra che una possibile soluzione è quella di tradurre la formula in una struttura ad
albero in cui gli OR sono le radici, gli AND i nodi intermedi e i confronti le foglie. Tale
albero verrà poi trasformato in un grafo direzionato e pesato secondo le seguenti regole:
• Il nodo di arrivo è quello che nel confronto sta dalla parte del minore
• Il nodo di partenza è quello che nel confronto sta dalla parte del maggiore
• Le costanti vengono sempre portate dal lato del nodo di partenza e costituiscono il
peso della connessione
• Una AND logica indica la connessione dei grafi, una OR invece una disgiunzione
dei grafi
• Qualora ci dovesse essere soltanto una costante e non un nodo di partenza/arrivo,
questo viene sostituito da un nodo detto “zero”
14
Da questi 5 semplici regole si traduce il vincolo prima in albero, poi in grafo. Ad esempio:
Figura 1.7: Esempio di conversione
RTL – grafo orientato
Questo grafo è desumibile a tempo 0 dalle specifiche temporale del processo, serve quindi
un algoritmo di analisi di tale task in riferimento ad una sequenza di occorrenze:
• Quando si verifica un occorrenza di un evento, il nodo del grafo corrispettivo viene
sostituito da un nodo “zero”
• Se il nodo tolto è un nodo di arrivo con ramo di peso P, allora il nuovo peso del
ramo sarà P-T, dove T è il tempo corrente.
• Se il nodo tolto è, viceversa, un nodo di partenza con ramo di peso P, allora il nuovo
peso del ramo sarà P+T.
• Il vincolo è violato ⇔ si crea un ciclo con peso negativo
È facile verificare che i vincoli dell’esempio sono violati a fronte della seguente sequenza
temporale di eventi:
Figura 1.8: Sequenza cronologica di eventi
15
a tempo t = 2, si ha il grafo orientato con ciclo negativo mostrato in figura:
Figura 1.9: Esempio di deadline sforata
che mostra evidentemente la violazione di almeno uno dei vincoli temporali.
16
Capitolo 2: Momentics per QNX-Neutrino
QNX è un RTOS Unix-like, che mira principalmente al mercato dei sistemi embedded. Il
sistema fu creato nei primi anni ’80 da G.Bell e D.Dodge, allora due studenti canadesi
durante la frequentazione del corso di Sistemi Operativi all’Univerisità di Waterloo in
Ontario.
Alla fine degli anni ’80 il sistema si muove verso una direzione che si sarebbe poi rivelata
la scelta giusta: l’adattamento allo standard POSIX nella major release QNX 4. La
successiva major release, fu rilasciata nel 2001 ed è di fatto la stessa base utilizzata ancora
oggi, QNX Neutrino, la cui differenza risiede principalmente nel completamento
dell’implementazione delle API POSIX e l’incorporamento nel proprio kernel anche
dell’implementazione dello standard RT-POSIX. Tuttavia, nel suo sviluppo e nella sua
storia, si vede una svolta decisiva nel momento in cui, nel 2010, QNX Neutrino fu
acquisito da “Research In Motion” (RIM), famosa per il brand mobile BlackBerry,
facendo di fatto approdare il sistema anche nel mercato mobile tramite il tablet “Playbook”
che monta proprio il RTOS canadese.
Contando già su un microkernel molto robusto ed affidabile, compatibile con numerose
architetture hardware (x86, ARM, PowerPC, MIPS, ecc.), i successivi aggiornamenti, fino
alla versione QNX Neutrino 6.6 del 2010, hanno offerto all’utente “soltanto” un
miglioramento prestazionale e l’aggiunta di funzionalità di grafica, di sicurezza, di
multimedialità, di sviluppo ed infine di monitoring, senza variare in maniera troppo spinta
17
il cuore del sistema. Tralasciando la trattazione nel dettaglio delle caratteristiche
fondamentali del sistema QNX, la quale esula dallo scopo di tale elaborato, si
analizzeranno adesso i meccanismi software messi a disposizione da RIM per il
monitoraggio del sistema.
L’idea di RIM è stata quella di voler integrare tutti i servizi per lo sviluppatore in un unico
tool di sviluppo, debugging e monitoraggio tramite un IDE Eclipse-based denominato
Momentics. Per quanto concerne l’aspetto del monitoring di sistema, in Momentics sono
implementati tre tool estremamente utili e potenti per l’analisi a run-time dei task attivi;
essi sono il “System Information”, il “System Profiler” e il “System Analysis Toolkit
(SAT)”.
2.1 System Information
Un primo set di tools per il monitoraggio è chiamato System Information. Si tratta di una
soluzione molto potente a causa della moltitudine di informazioni eterogenee che riesce a
Figura 2.1: System information
18
dare all’utente, pur rimanendo nel contesto di analisi di un unico processo alla volta. La
sua forza sta nell’estrema modularità, infatti è composto da tools che sono completamente
indipendenti tra loro, che ricavano un quadro informativo completo del task, fornendo
addirittura un meccanismo all’utente per poter inviare dei comandi a run-time al processo
selezionato oppure ad un suo thread.
In riferimento allo screenshot, è possibile notare la sua interfaccia estremamente userfriendly, infatti basta selezionare nella scheda “target navigator” dapprima la macchina
target, poi il processo da monitorare, per avere un set di informazioni (sulla destra)
riguardanti l’impatto del task sul sistema intero.
Analizzando nel dettaglio lo strumento, le informazioni fornite sono ripartite cinque
schede:
• Target Navigator, mostra la lista di tutte le macchine (reali o simulate) connesse
all’IDE e, di queste, ogni processo attivo. Tramite una voce del menu contestuale è
possibile mandare un “signal” al processo, che verrà poi gestito a seconda
dell’override (se presente) dell’handler dei segnali del task stesso.
• System Summary, è diviso in 3 schede; contiene informazioni riguardanti il sistema
in generale, quindi versione del RTOS, tipo di architettura e clock della CPU nella
prima sezione, un prospetto generale sia grafico che testuale della memoria nella
seconda sezione ed infine una lista dettagliata di tutti i processi del sistema, divisi
in locali e remoti, nella terza sezione.
• Process Information, è anch’esso diviso in tre schede; conserva informazioni
generali riguardo il processo selezionato. Nella prima sezione ci sono riferimenti ai
thread instanziati dal processo, con una moltitudine di informazioni a disposizione,
ad esempio è possibile vedere la priorità nella schedulazione, lo stato del thread
(quindi se è in attesa su una risorsa, se è in coda di schedulazione o se è attivo e sta
eseguendo), l’eventuale risorsa che il thread sta aspettando di ottenere, il
quantitativo di memoria stack utilizzata e tante altre. Nella seconda e terza sezione,
invece, le informazioni sono più orientate al sistema operativo, in quanto è
possibile vedere l’insieme delle variabili d’ambiente utilizzate e il descrittore del
19
processo.
• Memory Information, contiene informazioni dettagliate sulla memoria virtuale
assegnata al processo e le mostra a video in due modi: il primo è una raffigurazione
segmentale della memoria, divisa in parti di diverso colore per indicare le diverse
funzioni assunte dallo spazio di indirizzamento assegnato al task, in particolare
vengono mostrati lo stack, l’area codice, l’area heap, le shared library (cioè le
librerie caricate a runtime) e la memoria inutilizzata. Al di sotto di questa
raffigurazione, la stessa memoria viene mostrata anche testualmente, tramite
l’utilizzo di una tabella che mostra dettagliatamente ogni segmento di memoria
virtuale assegnata al processo come viene da questi utilizzato, mostrandone, tra le
altre cose, anche le dimensioni e il posizionamento all’interno dello spazio di
indirizzamento disponibile. Questa sezione è particolarmente utile per trovare i
cosiddetti “errori di stack”, cioè quegli errori derivanti da un cattivo utilizzo della
memoria stack che causa il malfunzionamento del programma per il salvataggio di
troppi record di attivazione e funzioni ricorsive troppo onerose in relazione alla
memoria disponibile.
• Malloc Information, è l’ultima scheda di monitoring del set di tools analizzato e si
focalizza sullo stato della memoria heap (il nome “malloc” rimanda proprio alla
system-call in C che alloca spazio in memoria heap). Una prima sezione riguarda
lo stato attuale della suddetta memoria, che viene visualizzata in forma grafica;
segue poi un contatore di quante volte sono state chiamate le funzioni di gestione
della memoria (cioè la malloc(), free(), realloc()), dati da cui si può desumere se il
processo sta effettivamente utilizzando troppa memoria senza rilasciarla, o sta
effettuando troppe system calls che rallentano l’esecuzione del task. Segue poi un
grafico, campionato in istanti di tempo richiesti, della situazione della heap, in
modo da capire il trend d’utilizzo della stessa. Infine è stata introdotta una tabella,
che analizza in dettaglio per un dato range di bytes quante malloc, free, realloc
sono state chiamate (se ad esempio il task richiede una malloc da x bit con x tra 65
e 128 bytes, allora il contatore relativo a questa fascia verrà incrementato di uno).
20
In definitiva, il tool System Information, fornisce delle informazioni riguardante lo stato
del sistema dal punto di vista del RTOS. Le informazioni ricavabili, sono di grossa
rilevanza, ma non sempre sono utili per la caratterizzazione completa di un
malfunzionamento o di una situazione ai limiti della criticità, proprio perché si osserva il
task dal di fuori, guardando solamente alle informazioni esterne che il task deve
obbligatoriamente (per ragioni di sicurezza ed adattamento al sistema globale) comunicare
al RTOS. Ciò che manca in questa suite è quindi un modo per poter accedere ai
meccanismi interni del task, dove risiedono, tra le altre cose, anche i requisiti temporali
che l’applicazione deve rispettare; infatti è bene notare che lo strumento appena analizzato
non fornisce in alcun modo un aiuto nel monitoraggio della componente temporale del
processo, ma soltanto per quella logica e sistemistica. Essendo QNX un RTOS, non
potevano non essere rilasciati dei meccanismi per poter affrontare la questione appena
proposta. Essi risiedono nelle componente software System Profiler (SP) e System
Analysis Toolkit (SAT).
2.2 SAT
Il System Analysis Toolkit è definito dagli sviluppatori di QNX come uno strumento che
va oltre il semplice debugger. Il progetto mira a fornire un quadro complessivo del sistema
senza tralasciare i meccanismi del RTOS a livello più basso.
Un semplice debugger è in grado di analizzare e registrare solo gli eventi riguardante il
contesto in cui viene eseguito, cioè ad esempio, se si sta effettuando il debug di un
processo, che, in un ben determinato istante di tempo chiama una funzione del kernel (un
allocazione di memoria ad esempio), non si riesce a vedere oltre il punto di vista del
processo analizzato: i meccanismi interni del sistema operativo sono trasparenti al
debugger, che quindi non potrà calcolare l’impatto che l’azione ha avuto sul sistema.
Ciò che gli ingegneri RIM decidono di offrire è un tool di analisi che permetta allo
sviluppatore di poter guardare il sistema dal livello del RTOS, ma mantenendo la
possibilità di capire come stia evolvendo al suo interno il task analizzato. Ironicamente, gli
21
sviluppatori del SAT affermano che questo strumento può fornire un “movie” (cioè un
filmato), laddove invece un semplice debugger può generare solo uno “snapshot” (cioè
una fotografia).
Risulta quasi evidente, che per poter offrire un campo di informazione così ampio e
dettagliato è necessaria una modifica del microkernel di QNX; parallelamente allo
sviluppo del kernel da utilizzare regolarmente (detto “release”), viene sviluppata una sua
versione modificata (detta “instrumentato”), che contiene alcuni moduli specifici per la
raccolta di informazioni, che verrà poi realmente effettuata da una componente software
separata.
Il kernel instrumentato, pertanto, intercetta informazioni riguardante i processi e li scrive
in un buffer, implementato come lista linkata circolare, da cui verranno prese e salvate da
una componente detta “Data Capture” ed interpretati da un’ulteriore modulo detto di “Data
Interpretation”
In dettaglio, il SAT è composto di quattro blocchi principali:
Figura 2.2: System Analysis Toolkit
• Il primo è proprio il kernel instrumentato, di fatto indistinguibile dal kernel release,
se non per la presenza dei moduli di raccolta di informazioni. Un ampio testing su
questo insieme di moduli ha dato come risultato che, in rapporto al kernel release,
l’impatto sul sistema provoca una perdita di velocità di esecuzione di circa il 2%.
• Il secondo blocco è il buffer del kernel, che, come detto, è realizzato mediante una
lista linkata circolare: il modulo di raccolta dati si occupa di scrivere le
22
informazioni all’interno di tale buffer, sovrascrivendo gli elementi più vecchi, nel
caso in cui esso dovesse essere saturo. Un gestore del buffer, infine, si fa carico di
segnalare alla componente successiva quando il livello dei dati è in prossimità di
saturazione (circa il 70% di occupazione di spazio disponibile).
• Il terzo blocco è il Data Capture (o anche “tracelogger”), un demone che riceve un
segnale dal gestore del buffer, che lo abilita al salvataggio delle informazioni
scritte in memoria. Il log che questo blocco andrà a comporre può essere scritto su
file o passato ad un device esterno, ma sarà in ogni caso un file binario che, in
questa fase, non ha un ben preciso significato. Lo stream di bit contenuti nel log
deve essere, a questo punto del processo di monitoraggio, interpretato.
• Il quarto e ultimo blocco che compone il SAT è proprio l’interprete del file binario
generato al punto precedente, detto anche “traceprinter”. Esso produce un vero e
proprio log, analizzabile dall’utente in cui sono riportati tutti gli eventi di sistema
ordinati per timestamp crescenti. Per quanto riguarda questo componente, RIM dà
allo sviluppatore la possibilità di poter effettuare un override delle sue funzioni, in
modo da definire le proprie originali funzioni di monitoring. È pertanto possibile,
grazie a questa flessibilità, trasformare il traceprinter in uno strumento per
analizzare in tempo reale i task, per mostrare risultati e statistiche globali del
sistema e altro ancora.
Dunque, in conclusione, il SAT è uno strumento di analisi del sistema che riesce, a
discapito di un piccolo overhead di sistema, penetrare nei meccanismi più interni del
sistema operativo e trarre informazioni utili all’azione di debug, come ad esempio il tempo
di servizio di una system-call, l’analisi della contesa delle risorse tra i vari task attivi (la
cosiddetta “race condition”, che può portare il sistema ad uno stato di attesa indefinita),
l’osservazione dello scambio di messaggi tra i thread e i processi, ecc. Queste
informazioni, non sono di norma facili da reperire, per cui è chiaro il grande contributo al
monitor che questo tool riesce a dare.
23
2.3 QNX System Profiler
Per QNX Neutrino, però, è stato sviluppato un ulteriore tool di analisi in tempo reale oltre
a SAT, che costituisce la punta di diamante della suite Momentics: il System Profiler (SP).
Come il tool precedente, anche il SP utilizza il kernel instrumentato nella raccolta di
informazioni di sistema come le chiamate al kernel, le interruzioni, i context-switch e le
variazioni alla coda di schedulazione. La forza di questo strumento sta nell’unire i
vantaggi del System Information (interfaccia user-friendly, vasta gamma di informazioni
anche in tempo reale) a quelli del SAT (utilizzo del kernel instrumentato per ottenere
informazioni che un semplice debugger come SI non potrebbe ottenere).
Nella vista principale, denominata target navigator, è possibile selezionare la macchina
target e dal menu contestuale selezionare la voce “Kernel Event Tracing”, per iniziare, per
mezzo del modulo kernel dedicato, la fase di raccolta dati, i cui campi d’interesse vengono
selezionati grazie al wizard che accompagna l’utente nella configurazione delle azioni di
monitoring desiderate. Gli eventi che il kernel può intercettare sono molto numerosi, tanto
da spingere gli sviluppatori di questo strumento a dividerli in macroclassi. Per ogni
singolo evento, l’utente può scegliere il livello di monitoraggio, infatti sono stati pensati
ben 3 livelli di dettaglio di informazioni: None (monitoraggio disattivato), Fast
(monitoraggio “informativo”, per una rapida segnalazione degli eventi, serve all’utente per
capire che un certo evento si è verificato, senza però fornirne i dettagli del contesto), Wide
(monitoraggio completo, per un set di informazioni il più dettagliato possibile, orientato al
debugging e alla ricerca di errori di sistema).
Le informazioni raccolte dopo l’avvio di questa procedura, vengono salvate in un file di
estensione .kev, associato al programma Momentics stesso in fase di installazione della
suite. Il log viene visualizzato ed analizzato tramite il “SP editor”, che fornisce dati, tempi
di esecuzione, statistiche, grafici e proiezioni dei task analizzati.
Una funzione sicuramente molto utile in fase di monitoraggio di sistema è la “CPU
Activity Presentation”, che centra appieno l’obiettivo di fornire una GUI tanto semplice,
quanto potente dal punto di vista dei dati di analisi presentati, infatti mostra una timeline
24
segmentata dell’esecuzione globale dei task schedulati, disponibile in forma lineare (come
in figura 2.3), di unica barra divisa per colori, di grafico a torta o istogramma, sia in due
che in tre dimensioni.
Figura 2.3: CPU Activity Presentation
Il SP è, per concludere, un insieme piuttosto vasto di funzioni dedite al monitoring, che
vanno dalla ricerca dei richiedenti e degli utilizzatori delle strutture IPC, al calcolo tempi
di esecuzione di ogni task, da moduli di ricerca avanzata per trovare in modo agevole un
particolare evento in un log anche piuttosto complesso, fino ad una tabella delle proprietà
e dello stato di ogni singolo thread di ogni task in tempo reale; questo potentissimo
strumento col proseguire degli aggiornamenti delle suite Momentics migliora in maniera
esponenziale, facilitando estremamente il compito dell’utente che si riduce (nei casi più
semplici) ad una semplice osservazione di una tabella.
25
Capitolo 3: Monitoring per VxWorks
VxWorks è il RTOS commerciale più diffuso al mondo ed è sviluppato dalla compagnia
software “Wind River” di Alameda, California. Esso è utilizzato per gestire in tempo reale
molti tra i dispositivi hardware più complessi mai creati dall’uomo, basta infatti pensare
che, per portare un unico esempio, VxWorks è il RTOS utilizzato su “Curiosity”, il
“laboratorio spaziale” inviato su Marte. È pertanto ritenuto, oltre che soddisfacente in
termini prestazionali, anche estremamente sicuro e robusto a disturbi ed imprevisti che di
certo sono presenti in un progetto da inviare su un altro pianeta.
La sua introduzione sul mercato ha una storia meno “lineare” rispetto al concorrente QNX:
nasce nei primi anni ’80 come un set di migliorie per il già esistente sistema operativo
VRTX di Ready Systems (di cui Wind River aveva acquisito i diritti di distribuzione), il
quale, anche se era venduto come un RTOS, non riusciva a soddisfare nei suoi 4KB di
kernel i requisiti che un sistema operativo in tempo reale deve avere; forniva cioè
solamente un abbozzo di quello che sarebbero poi diventati i RTOS. Da questa mancanza
nasce il nome VxWorks, che si pensa sia un acronimo per indicare il gioco di parole “VrtX
that WORKS”, ovvero una rivisitazione di VRTX, ma che stavolta funzioni davvero.
Rotti, in seguito, gli accordi commerciali tra Ready Systems e Wind River, la casa
californiana decide di continuare a battere il sentiero già intrapreso, sviluppando di fatto
un microkernel che avrebbe poi sostituito VRTX e vendendo un sistema operativo realtime comprensivo di microkernel e applicazioni di sistema e di sviluppo tutte prodotte da
Wind River.
26
La grandezza di questo RTOS sta nell’aver anticipato i suoi concorrenti nell’offrire
all’utente finale delle funzionalità che altri sistemi operativi stavano ancora ricercando,
infatti, ad esempio, VxWorks è stato il primo RTOS a introdurre il supporto real-time allo
stack di rete ed implementare, nella loro versione in tempo reale, protocolli di
comunicazione come CAN, Bluetooth e Firewire. Le funzionalità offerte, rimanendo in
una descrizione introduttiva generale ad ampio raggio, lo rendono sicuramente un sistema
molto completo, infatti per quanto riguarda lo scheduler, implementa un Round-Robin a
256 priorità fisse preemptive, per avere la giusta predicibilità dell’invarianza di priorità e
al tempo stesso la dinamicità della schedulazione RR; per quanto riguarda, invece, il
kernel, esso divide i task in tre categorie fondamentali, riuscendo ad isolare i task RT, da
quelli non RT e dalle routine di sistema; per quanto riguarda, infine, le strutture messe a
disposizione per la comunicazione dei task (IPC), sono fornite tutte le tipologie di
semafori, persino con il protocollo Priority Inheritance, di solito scartato nelle applicazioni
real-time, data la sua complessità, inoltre ci sono anche code di messaggi, tra thread, tra
task e anche tra sistemi operativi, poiché VxWorks offre il supporto alla virtualizzazione
dell’hardware, così da poter installare più sistemi operativi (anche non real-time) sulla
stessa macchina.
Essendo un sistema così diffuso ed utilizzato, negli ambiti più disparati, Wind River ha
dovuto provvedere a fornire un corredo software al RTOS, per lo sviluppo e l’analisi dei
task attivi nel sistema. Come per la soluzione QNX già analizzata, anche Wind River ha
optato per un IDE all-in-one, che permetta all’utente di ricoprire il ruolo di sviluppatore,
debugger e analizzatore di sistema in un unico programma, battezzato col nome di
“Tornado” per VxWorks fino alla versione 5 e poi di “Wind River Workbench” a partire
dalla versione 6.
I tool di monitoraggio per VxWorks sono davvero tantissimi, quindi, a titolo
esemplificativo, verranno adesso mostrati solo alcuni di essi, andando ad effettuare una
analisi “a campione” tra tools dell’odierna suite Workbench, altri della precedente
piattaforma Tornado ed infine altri ancora che non sono sviluppati da Wind River, bensì
da compagnie satellite, in partnership con la casa produttrice del RTOS.
27
I tools scelti “CoverageScope” e “MemScope” per Workbench, poi “Spy” e “WindView”
di Tornado ed infine “Tracealyzer” sviluppato dalla software house Percepio (che ha
sviluppato lo stesso tool anche per altri RTOS, come ad esempio RTLinux e FreeRTOS).
3.1 Monitoring per Workbench
3.1.1 CoverageScope
CoverageScope è uno strumento di analisi dinamica appartenente all’ambiente
Workbench, che permette al programmatore di verificare la copertura del codice nei casi
di test previsti e sviluppati. Ciò che lo rende diverso da un semplice analizzatore ti
copertura del codice è il suo orientamento al real-time: è infatti possibile gestire i dati
forniti in uscita da CoverageScope in tempo reale, durante la stessa esecuzione del caso di
test. Nella view principale viene mostrato il file oggetto analizzato con affianco un
riepilogo (in forma percentuale) delle chiamate a funzioni, segnalazione di ingresso e
uscita dai blocchi monitorati e hit-ratio per la condizione degli “if”. Dal menu contestuale
si possono ricavare delle informazioni più dettagliate, avendo a disposizione una lista di
tutte le funzioni e i blocchi presenti nell’oggetto. È possibile inoltre controllare il codice a
run-time, che sarà evidenziato per indicare un’eventuale copertura degli statement.
L’unica operazione non possibile è la modifica run-time del codice, perché servirebbe una
ricompilazione del sorgente. Per dare, infine, un’ulteriore aiuto all’interpretazione dei dati,
queste statistiche sono visualizzabili anche in forma di istogramma o di trend-graph.
Figura 3.1: CoverageScope
28
3.1.2 MemScope
Il MemScope è uno strumento di analisi in tempo reale integrato nella suite Workbench
per vedere l’evoluzione della memoria centrale del sistema target e come essa viene
utilizzata, alla ricerca di memory-leaks o di eccessiva occupazione della stessa.
È abilitato di default nel simulatore di VxWorks, per cui in fase di testing delle
applicazioni real-time, la configurazione dello strumento è totalmente trasparente
all’utente che dovrà solo selezionare il MemScope e utilizzarlo. Questo strumento fornisce
una visione estremamente dettagliata della memoria, facendo riferimento anche al codice
sorgente (se disponibile), infatti, in fase di running del task, MemScope riesce ad
intercettare tutti gli eventi e le system-calls relative alla gestione della memoria, come ad
esempio malloc, free, realloc, new e delete. Registrate per ogni thread del task tali
informazioni, riesce a dare un quadro informativo completo sia nel generale che nel
particolare dell’utilizzo della memoria: presenta una tabella con il numero di allocazioni e
de-allocazioni chiamate, il numero di byte allocati in area heap per il thread, quanti byte
sono stati rilasciati ed infine un rapporto (espresso sottoforma di percentuale) della
memoria rilasciata rispetto a quella richiesta. MemScope, però, non si limita ad analizzare
nel complesso l’utilizzo di memoria del task e dei relativi thread, infatti riesce a registrare
anche il nome di ogni funzione chiamante, la sua posizione nel codice sorgente (sia file
vero e proprio .c, .cpp, ecc, sia numero di linea), i byte richiesti dal blocco e se e quando
essi sono stati rilasciati. In questo modo, a valle di funzionamenti non previsti, con un
semplice doppio-click sulla linea del log che segna l’allocazione indesiderata, l’utente può
risalire all’esatta linea di codice che ha generato la situazione anomala e risolvere il
problema.
3.2 Monitoring per Tornado
Tornado è la piattaforma di analisi e sviluppo che è stata soppiantata da Wind River
Workbench. La migrazione alla nuova piattaforma, ha obbligato Wind River ad un refactoring di tutti gli strumenti utilizzati da tornado per questioni di retro compatibilità, per
29
cui è bene ricordare che gli strumenti che saranno ora analizzati sono presenti come plugin
nel Workbench, anche se i tool non sono esattamente gli stessi, ma degli adattamenti, che
li rendono sicuramente meno performanti rispetto gli originali su Tornado.
3.2.1 WindView
Il WindWiew è il tool su cui Wind River punta per l’analisi dinamica del sistema target,
infatti è anche quello più documentato, nonché quello più pubblicizzato ed esaltato dalla
stessa casa americana, che nella nuova versione presente in Workbenh ha ribattezzato
come “System Viewer”. Si tratta di un monitor a 360°, utilizzato per monitorare i più
svariati tipi di eventi in corso nel sistema. Consiste di due azioni fondamentali, nella prima
parte tramite il tasto di start/stop si abilita/disabilita la raccolta di dati in un file di log. In
questa fase, c’è bisogno di scegliere quali eventi devono essere selezionati ed è un’azione
fortemente consigliata dalla documentazione stessa, in quanto la fitta varietà di eventi
disponibili può creare confusione nel file di log. Eseguita la raccolta dei dati, in maniera
totalmente offline può essere visualizzato e studiato il log, sempre per mezzo dello
strumento WindView, che dà all’utente la possibilità di analisi sia in forma grafica sia in
forma testuale. La schermata presentata è molto intuitiva, in quanto è disegnata una linea
in corrispondenza del task schedulato. Come detto, gli eventi sono molteplici e in ugual
numero i simboli corrispondenti che vengono posizionati su tale grafico, la cui semantica
viene specificata da una legenda ottenibile da una semplice scorciatoia nel menu. Se però
la forma grafica fosse scomoda, o di difficile interpretazione (a causa dei molti task in
esecuzione o dei troppi simboli visualizzati, ad esempio) è sempre possibile ottenere le
stesse informazioni in forma tabellare, con delle entry in tabella contrassegnate dai
timestamp, le quali indicheranno gli eventi corrispondenti a quale task afferiscono, il
tempo di esecuzione e il contesto in cui eseguono.
Lo strumento presenta anche un ottimo modulo di ricerca, che permette di trovare un
particolare evento sia tra tutti i task sia relativamente ad un solo processo, contare le
occorrenze dell’evento considerato e, qualora possa essere di interessa, anche salvarlo e
dare ad esso un nome per poterlo facilmente ritrovare nel seguito.
30
WindView, in definitiva, rappresenta lo strumento di analisi definitivo del sistema, in
quanto riesce a catturare ogni tipo di evento e tracciare un grafico di utilizzo medio o di
picco di tutte le risorse a disposizione nel sistema, il tutto con un minimo impatto sullo
stesso, dal momento che l’overhead è rappresentato solamente dal task che si occupa di
raccogliere le informazioni, poiché queste poi verranno analizzato in differita
dall’esecuzione dei task analizzati.
Figura 3.2: WindView
3.2.2 Spy
Spy è un tool di monitoraggio per VxWorks che è distribuito sia come applicativo standalone che integrato nella suite Tornado e serve ad analizzare l’utilizzo della CPU da parte
dei task. Lo strumento spy è molto semplice, ma offre delle informazioni molto utili per
capire se si sta sovrastimando o sottostimando il sistema in termini potenza elaborativa.
Nell’unica view di cui è composto, Spy mostra l’elenco dei task attivi nel sistema, più le
voci “KERNEL”, “INTERRUPT” e “IDLE”, le quali descrivono la situazione in cui si sta
eseguendo del codice in kernel mode, si sta gestendo un’interruzione e quando il sistema è
in stato di idle (cioè non ci sono task attivi). Affianco ad ognuna delle voci della lista è
presente un barra che segnala, in rapporto al tempo di osservazione, la percentuale di
utilizzo del processore.
Spy presenta sostanzialmente due svantaggi: il primo è legato, come ogni azione attiva di
monitoring dinamico, all’impatto che il task di controllo ha sul sistema, infatti Spy è
comunque un task e in quanto tale provoca overhead al sistema, poiché esso deve essere
31
servito. Per questo tipo di controllo, che è dedicato al consumo sulla CPU e non sulla
memoria (che Spy, così come tanti altri tool di monitoring, stressa in minima parte), la
considerazione sull’overhead è estremamente più critica, rendendo di fatto Spy un tool
inutilizzabile su sistemi real-time dotati di capacità elaborative minime: in pratica, sebbene
le informazioni di raccolta di Spy non siano particolarmente difficili in termini di
consumo, bisogna conoscere il contesto in cui si esegue, altrimenti Spy potrebbe generare
risultati falsati. Per rendere ancor più leggero tale strumento, si è pensato allora di non
fornire un supporto real-time in termini di “tempo continuo” (per quanto possa
considerarsi “continua” la schedulazione dei task da parte della CPU, che come risaputo
esegue in maniera esclusivamente sequenziale), bensì di campionare il tempo di
osservazione e fornire risultati del tipo “uno ogni x millisecondi”, che, se da un lato, come
detto, alleggerisce le operazioni da svolgere, dall’altro introduce tutti i problemi
ampiamente affrontati nella teoria delle telecomunicazioni riguardanti il campionamento.
3.3 Tracealyzer
Come anticipato, VxWorks vanta una miriade di compagnie software acquisite,
indipendenti, o legati da accordi commerciali, che si occupano di sviluppare software di
vario genere per il RTOS di Wind River. I software prodotti spaziano su ogni genere, ma
in particolare è certamente di grande interesse l’applicativo Tracealyzer di Percepio.
L’obiettivo di questo strumento è di fornire all’utente un debugger che sia orientato
all’interpretazione di un log di un sistema real-time, che registra tempi di esecuzione dei
task, chiamate al kernel, routine di servizio delle interruzioni, comunicazione con le
strutture IPC, ecc.; per questo motivo, in Tracealyzer sono implementate più di venti
forme di visualizzazione, ognuna delle quali mette in luce un aspetto differente delle
informazioni raccolte. Il log file che viene analizzato, deve essere disponibile al
programma, cioè Tracealyzer analizza il log, ma non lo produce. Per effettuare una cattura
degli eventi, Percepio stessa suggerisce di utilizzare il trace recorder contenuto nella
libreria “wvlib”, fornita nell’immagine stock del kernel VxWorks 5.5+. Il log può essere
32
sia costruito ed analizzato “al volo” utilizzando come destinazione un buffer circolare in
memoria centrale, oppure dapprima salvato in uno o più file su disco e poi analizzato con
Tracealyzer in un secondo momento.
La schermata principale del programma è estremamente intuitiva, ma in ogni caso carica
di informazioni, infatti su una timeline verticale vengono posizionati i task con dei
rettangoli colorati a seconda del tipo (user, kernel, interrupt, ecc), ai quali vengono
collegate delle note in cui viene descritto l’evento che è stato registrato. Selezionando un
evento è possibile inoltre ottenere tutte le informazioni (in forma non più grafica, ma
tabellare) dell’occorrenza dell’evento.
Figura 3.3: Tracealyzer, main view
Una seconda schermata degna di nota è la “CPU Load Graph” che ha una funzione simile
a quella del tool Spy, in quanto riesce a dare le percentuali di utilizzo dei task, delle ISR e
del codice kernel in forma sia testuale che come istogramma, con barre colorate usando la
stessa convenzione cromografica della main view.
Un ultimo grafico analizzato è il “Task Communication Flow” che, da un’analisi delle
strutture IPC e dei dati condivisi tra i vari task, riesce a disegnare un Flow Diagram che
collega i vari processi tra loro e tra le strutture condivise, diagramma davvero molto utile
nel caso si ricerchi la fonte di un deadlock, fenomeno che dalla teoria dell’informazione è
dovuto all’attesa circolare sulle risorse protette.
33
Conclusioni
Nello sviluppo dell’elaborato è stato presentato uno studio teorico, che fonda le sue basi
sul concetto di automonitoraggio dei task. È lecito, dunque, chiedersi quale delle due
soluzioni (attiva o passiva) sia la migliore. Evidentemente, per coesistere, non c’è una
soluzione migliore, ma solo dei vantaggi e svantaggi in trade-off tra loro. Il monitoraggio
attivo porta con sé il vantaggio di non introdurre complessità nel modello fondamentale
del sistema, in quanto si tratta semplicemente di task che accedono ad una memoria
comune; questo vantaggio però si traduce inevitabilmente in un accoppiamento maggiore
(rispetto al caso duale) tra le funzionalità operative e di monitoraggio, portando nel
complesso overhead al sistema, anche quando l’azione di monitoring non è desiderata.
Dall’altra parte, invece, la complessità sistemistica maggiore della soluzione passiva, offre
delle performance più spinte e un grado di indipendenza maggiore (ma non totale). Sta
dunque al sistemista capire quale sia il contesto in cui operare e decidere quale delle due
soluzioni è più indicata per il sistema da costruire.
Nel seguito sono stati analizzati degli strumenti concreti di analisi dei sistemi esistenti,
mostrando in che modo un operatore può agire sul sistema al fine di monitorarlo e
l’impatto computazione che questa azione ha sul sistema complessivo.
In conclusione, il monitoraggio di un sistema in tempo reale è un’operazione di un valore
tanto maggiore, quanto più è safety-critical il sistema da controllare; è un’azione
34
fondamentale, che dovrebbe essere svolta in ogni sistema in tempo reale, anche quello
idealmente più robusto e difficile da distruggere, poiché, sebbene l’informatica sia una
scienza certa, è sempre bene ricordare la cosiddetta legge di Green, la quale, consigliando
ai sistemisti di ragionare sempre nel worst-case, recita «Se un sistema è stato concepito
per tollerare un insieme di guasti, ci sarà sempre un idiota abbastanza ingegnoso da
causare un guasto non tollerato», pertanto l’azione di monitoring permette, a volte, di
intervenire prima che il suddetto “idiota” abbia causato una catastrofe.
35
Bibliografia
[1]
Giorgio Buttazzo, Sistemi in Tempo Reale, Pitagora Editrice, 2006.
[2]
S. E. Chodrow, F.Jahanian, M. Donner, Run-Time Monitoring of Real-Time
Systems, Real-Time Systems Symposium, 74-83, 1991.
[3]
QNX Development Support, Getting system information,
http://www.qnx.com/developers/docs/6.3.0SP3/ide_en/user_guide/sysinfo.html,
08/02/15
[4]
QNX Development Support, Analyzing your system with kernel tracing,
http://www.qnx.com/developers/docs/6.3.0SP3/ide_en/user_guide/sysprof.html,
08/02/15
[5]
QNX Momentics Development Suite, System Analysis Toolkit (SAT) User’s guide
for QNX Neutrino 6.2.0 or later, Documentazione ufficiale di QNX Neutrino.
08/02/15
[6]
QNX, http://en.wikipedia.org/wiki/QNX, 07/02/15
[7]
VxWorks, http://en.wikipedia.org/wiki/VxWorks, 10/02/15
[8]
Wind River System Viewer User’s Guide 3.0, Documentazione ufficiale di
VxWorks 10/02/15
[9]
Wind River Workbench Overview, http://www.windriver.com/products/productoverviews/PO_VE_3_8_Platform_1209.pdf, 10/02/15
[10] Wind River Demo
http://www.windriver.com/products/development_suite/wr_demos, 10/02/15
[11] Tracealyzer for VxWorks,
http://www.phaedsys.com/principals/percepio/tracealyzervxworks.html, 10/02/15
36