Alcuni anni fa, durante la tesi universitaria, mi ero occupato di analisi statica, un argomento che mi è sempre stato a cuore. Ma di che cosa si tratta?
L’analisi statica, in generale, è una tecnica automatica che deduce dei risultati “interessanti” dallo studio del codice del programma, sia esso il codice sorgente, il bytecode, il codice oggetto, o qualunque altra rappresentazione intermedia generata dal compilatore. L’analisi statica normalmente è eseguita appena prima della fase di compilazione, per questo si dice che è eseguita a compile time. Essa si contrappone alle analisi dinamiche compiute a run time, cioè durante l’esecuzione del programma.
Senza dilunguarci troppo, la cosa fondamentale da comprendere è che l’analisi statica permette di raccogliere informazioni riguardo al comportamento di un programma in fase di esecuzione senza che questo venga realmente eseguito. Essa è adoperata per moltissimi scopi: dai compilatori per generare codice più performante e compatto, ai sistemi (come quello che vediamo in questo articolo) che permettono di ricercare in automatico dei bug nei codici sorgente.
A partire dalla versione 3.2, è stato integrato in Xcode un analizzatore statico open source di alta qualità denominato Clang Static Analyzer. Esso, collegandosi al front end Clang del compilatore LLVM, permette di trovare alcuni problemi nei file sorgente C e Objective C direttamente a compile time (quindi senza dover eseguire il programma), mostrandoli in modo analogo ai warning o agli errori di compilazione:

In questo articolo vedremo come sfruttare questo potente strumento e, soprattutto, che cosa possiamo aspettarci da lui.
Come faccio a lanciare Clang Static Analyzer sul mio codice?
Niente di più semplice. In Xcode 4 basta scegliere Product > Analyze:

L’analizzatore statico è davvero ben integrato in Xcode: non dobbiamo usare un tool separato e possiamo richiamarlo con una singola scorciatoia da tastiera (⇧⌘B). Inoltre:
- Funziona sia sui progetti Mac sia su quelli iOS (iPhone e iPad).
- Non richiede alcuno sforzo per predisporre il codice sorgente all’analisi.
- E’ veloce (appena un poco più lento della semplice compilazione).
- Genera dei resoconti di semplice comprensione.
L’analisi statica viene effettuata sulla configurazione di debug. Eventualmente possiamo eseguirla su un’altra configurazione andando nella pagina Edit Scheme:

Che tipo di problemi trova?
Clang Static Analyzer identifica questi problemi:
- Errori logici (lettura di variabili non inizializzate, deferenziazione di puntatori a null, divisioni per zero, codice mai eseguito, attributi mai utilizzati, etc.).

- Errori nella gestione della memoria (memory leak, implementazione errata del metodo dealloc, etc.).

- Mancato rispetto di linee guida o convenzioni importanti (metodo il cui nome non inizia con init o copy che restituisce un oggetto non autoreleased, etc.).

Le segnalazioni più utili sono sicuramente quelle relative agli errori nella gestione della memoria, che possono capitare anche agli sviluppatori più esperti e attenti.
Come faccio a trovare la causa di un problema segnalato?
Può capitare che a volte una segnalazione non sia del tutto autoesplicativa. C’è un trucco, poco conosciuto, per farci aiutare dallo stesso Clang Static Analyzer. Per vederlo, facciamo un esempio concreto:

Il problema è banale: nella riga 21 viene copiata la NSMutableString che poi viene assegnata ad una NSString che non viene mai rilasciata. Per un attimo però facciamo finta di non averlo ancora capito. Per prima cosa facciamo click sull’icona blu appena a sinistra del messaggio di warning:

Come per magia viene espanso il flusso logico che evidenzia ciò che l’analizzatore ha trovato:

La causa del problema diventa evidente: alla riga 21 il metodo copy restituisce un oggetto con retain count a 1, che però non viene mai rilasciato.
Se le frecce ci sembrano confusionarie, possiamo affidarci alla barra che compare appena sotto la jump bar e che permette di andare avanti passo passo:

Niente male, non è vero?
Ma quanto è affidabile?
L’analisi statica, per sua natura, non potrà mai essere perfetta, perché deve necessariamente compiere alcune approssimazioni. Quindi, per quanto si usino tool sofisticati, ci saranno sempre dei bug che non potranno essere rilevati (falsi negativi) o, viceversa, delle segnalazioni su codice corretto (falsi positivi).
Detto questo, perché un analizzatore sia utile nella pratica, deve mostrare il minor numero di falsi positivi, per non richiederci un massiccio lavoro di filtraggio, e di falsi negativi, perché in caso contrario potremmo avvertire un falso senso di sicurezza (che in alcuni casi è perfino più pericoloso della presenza accertata di problemi).
Clang Static Analyzer, da questo punto di vista, è sicuramente molto valido. Per questo a parer mio dobbiamo abituarci ad usarlo con regolarità e a rendere il nostro codice “warning free”: se, attraverso l’analizzatore statico, individuiamo un bug molto presto, potremo risparmiare parecchio tempo durante i test, quando le cause degli errori sono più difficili da individuare.
E’ interessante osservare che l’analizzatore statico considera ogni possibile flusso di esecuzione, anche quelli che si verificano solo nei casi più rari. Ecco perché in alcuni casi può aiutarci a scovare problemi che attraverso i test a run time non avremmo mai trovato!
Naturalmente dobbiamo tenere sempre bene a mente che anche se Clang Static Analyzer non rileva nulla, non vuol dire che non ci siano errori. Per questo, oltre all’analizzatore statico dobbiamo usare tutte le possibiliti tecniche complementari: dai test automatici agli strumenti per l’analisi a run time.
Per quanto riguarda i falsi positivi, invece, una volta che avremmo accertato che sono davvero tali, possiamo selettivamente escuderli inserendoli in un blocco #ifndef:
#ifndef __clang_analyzer__ // Code not to be analyzed #endif
Per concludere aggiungiamo che Clang Static Analyzer supporta molte “annotazioni” sotto forma di attributi e pragma che possono aiutarlo a rendere le sue analisi più accurate. Per maggiori informazioni rimandiamo alla documentazione ufficiale.
L’autore
Valerio Dutto. Ingegnere informatico con oltre 5 anni di esperienza nella realizzazione di software professionali. Dal 2009 si occupa a tempo pieno di sviluppo su piattaforme iOS e Mac OS X. Ha contribuito a oltre 15 applicazioni iPhone e iPad, alcune delle quali hanno ricevuto importanti riconoscimenti. Il suo sito Web è http://www.valeriodutto.com.









6 Responses to “Xcode 4: scopriamo come possiamo usare l’analizzatore statico per trovare alcuni bug nel nostro codice”
27 Maggio 2011
SimoneComplimenti! Come sempre ottimo articolo!
27 Maggio 2011
alessandroun potential leak è causa di rifiuto da parte di apple? anche se è solo potenziale?
articolo molto utile
27 Maggio 2011
Valerio DuttoSono contento che l’articolo vi sia piaciuto.
Alessandro: un memory leak difficilmente (a meno che non sia proprio evidente) causa il rifiuto dell’app da parte di Apple. Tuttavia è importante cercare di non lasciarne nemmeno uno: è questo ciò che distingue un’applicazione professionale da una amatoriale.
1 Giugno 2011
DomenicoComplimenti per l’articolo, davvero utile.
Ho fatto qualche prova oggi con un progetto su cui sto lavorando ma ho avuto un problema: le prime volte che ho lanciato l’analisi tutto funzionava come descritto nell’articolo. Poi ho continuato a lavorare sul codice (senza toccare le impostazioni di Xcode) ma adesso quando faccio l’analisi mi vengono mostrati i warning e le indicazioni dell’Analyzer nella colonna del Navigator ( a sinistra quindi) ma nel codice non compare nessuna segnalazione accanto alla riga incriminata. Se vado a cliccare sulle icone blu o gialle dei warning mi si apre il file interessato ma non mi appare comunque nessun simbolo sulla riga.
Ci deve essere qualcosa che mi sfugge ma ci sto impazzendo. Potete darmi una mano? Grazie
5 Giugno 2011
redsendBell’articolo, è veramente importante analizzare un programma con questo genere di strumenti… ottimo lavoro!
6 Giugno 2011
Giuseppe LanzaPotenziale è un modo carino di dirti “stai sbagliando”. Non so se implica la non accettazione da oarte di apple, ma so per certo che sarebbe meglio risolvere, per non incorrere in memory warning. Un’app con questi leak “evidenti” (perchè se li rova questo analyzer sono leak evidentissimi) non è una buona app. Risolvi, capisci l’errore e non rioeterlo.