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