Introduzione
Il debugger è un potente strumento mediante il quale è possibile osservare il comportamento del programma in fase di esecuzione (run-time) allo scopo di individuare i malfunzionamenti di tipo logico. Spesse volte viene anche utilizzato per capire il funzionamento del programma o di alcune funzioni particolarmente critiche o descritte in maniera lacunosa nei documenti di progetto. Mediante questo strumento è possibile interrompere o sospendere l'esecuzione del programma per analizzarne il codice, monitorare e modificare le variabili, visualizzarne i registri, esaminare le istruzioni create dal codice sorgente e lo spazio di memoria utilizzato dall'applicazione. In sintesi si ha un completo controllo del codice e delle variabili/registri utilizzate.
A tale scopo è possibile utilizzare le varie voci presenti nel menu View per potere visualizzare le informazioni (stack, memoria di programma, registri speciali ecc.) di interesse.
Per potere utilizzare il debugger il programma deve essere compilato in modalità Debug. Tale modalità introduce del codice che serve per il funzionamento del debugger. In generale è preferibile evitare di introdurre, almeno volontariamente, del codice alterando in maniera significativa il codice oggetto che deve essere caricato sul target.
Di seguito si farà riferimento alla versione 8.8 dell’ambiente MPLAB ed il compilatore C HI-TECH PICC 9.83. I tasti funzione associati ai vari comandi o tool bar sono quelli impostati per default dall'ambiente integrato MPLAB in fase di installazione. E’ opportuno notare che un’istruzione scritta in linguaggio C può comportare la generazione di zero o più istruzioni in codice assembler, facilmente ispezionabili tramite la finestra del codice disassemblato ( menu View-->Disassembly Listing).
Debugger e Simulatore
Per attivare il debugger ed il simulatore del microcontrollore utilizzato per la realizzazione del codice, bisogna definire quale tool si vuole utilizzare per la simulazione del microcontrollore (MPLAB SIM) e compilare il codice in modalità Debug. La prima operazione può essere effettuata utilizzando il menu Debugger-->Select Tool-->MPLAB SIM, per la seconda operazione è sufficiente selezionare l'opzione Debug.
E’ opportuno anche definire la frequenza di oscillazione che il simulatore utilizzerà per l'esecuzione delle istruzioni. A tale scopo dal menu DebuggerSettings… verrà visualizzata la seguente dialog da cui è possibile impostare il valore corretto dal tab “Osc / Trace” (4 MHz nel nostro caso).
Durante una sessione di debug si utilizzano varie icone per indicare le azioni che si stanno compiendo ed i comandi più usati. In particolare per indicare l’istruzione corrente si usa il simbolo e per rappresentare la posizione in cui si trovava il debugger prima del lancio del comando Run. Entrambe compaiono a sinistra della finestra contenente il codice sotto test.Comandi fondamentali del Debugger
I comandi fondamentali del debugger che saranno descritti nel seguito sono:
- Run. Tale comando esegue il programma interrompendosi solo se incontra un breakpoint.
- Halt. Tale comando interrompe l’esecuzione del programma e lo fa transitare in uno stato di pausa. E’ possibile riprendere successivamente l’esecuzione del programma.
- Animate. Tale comando esegue il run in maniera rallentata in modo da potere osservare l’evoluzione dello stesso sia step by step e sia nella visualizzazione delle variabili/registri.
- Step Into. Tale comando consente di proseguire il programma step by step. Se si è posizionati su una funzione il debug entra all’interno della stessa.
- Step Over. Tale comando esegue un’istruzione oppure una chiamata ad una funzione senza entrare all’interno della stessa.
- Step Out. Tale comando consente di posizionarsi all’istruzione successiva della funzione corrente.
- Reset. Tale comando effettua il reset del programma.
- Breakpoints. Tale comando visualizza una dialog per potere inserire dei breakpoints semplici a specifici indirizzi di memoria.
Oltre a questi è possibile utilizzare il tasto destro del mouse per potere usufruire di altri due comandi.
- Run To Cursor. Tale comando consente di eseguire le istruzioni fino al punto in cui si è posizionati con il cursore del text editor.
- Set PC at Cursor. Tale comando consente di posizionarsi al punto corrente.
Comando Run
Tale comando rappresentato dall’iconanella toolbar esegue il programma corrente alla velocità normale, interrompendosi solo se incontra un breakpoint oppure viene lanciato il comando Halt. E’ possibile utilizzare anche il tasto F9 oppure tramite il menu Debugger-->Run.
Una volta lanciato il comando, il codice viene eseguito e nella parte inferiore a destra di MPLAB compare la seguente barra di progressionead indicare l’esecuzione del programma. Durante l'esecuzione del comando non vengono aggiornate le variabili/registri monitorate.
Comando Halt
Tale comando rappresentato dall’iconanella toolbar interrompe l’esecuzione del programma. E’ possibile utilizzare anche il tasto F5 oppure tramite menu Debugger-->Halt. Una volta lanciato il comando, il debugger interrompe l’esecuzione del programma e si posiziona sull’istruzione che stava eseguendo. E' possibile riprendere l'esecuzione si atramite i vari comandi step e sia lanciando il comando run. E’ opportuno considerare che utilizzando un linguaggio di programmazione come il C, una volta lanciato il comando, il cursore del debug si può posizionare su una istruzione che non è detto che sia la prima istruzione assembler ottenuta dalla compilazione dell’istruzione C. Per esempio lanciando il comando, il debugger interrompe la sua elaborazione alla seguente istruzione
andando ad esaminare il disassemblato
da cui si evince che il cursore si è fermato alla terza istruzione assembler ottenuta dalla compilazione dell’istruzione.
Comando Animate
Tale comando rappresentato dall’iconanella toolbar esegue il run in modo da potere osservare l’evoluzione del programma in maniera rallentata (step by step). E’ possibile lanciare il comando tramite il menu Debugger-->Animate. Una volta lanciato il comando, il debugger esegue le istruzioni in modo da consentirne la visualizzazione ed aggiornamento delle istruzioni/dei registri/delle variabili. La velocità di aggiornamento (e quindi di esecuzione delle istruzioni) può essere modificata tramite la dialog attivabile tramite il comando Debugger-->Settings…-->Animation/Realtime Updates
Spostando lo slider è possibile agire sulla velocità di esecuzione del programma. Dopo ogni step sarà aggiornato i valori delle variabili/registri/watch di tutte le finestre aperte nell’IDE.
Comando Step Into
Tale comando rappresentato dall’iconanella toolbar consente di proseguire il programma entrando in una funzione. E’ possibile utilizzare anche il tasto F7 oppure il menu Debugger-->Step Into. E’ un comando importante dal momento che ci consente di effettuare il debug andando all’interno delle funzioni e monitorare i parametri di scambio e la corretta esecuzione della stessa. Eseguendo questo comando dalla view contenente il codice sorgente C, il risultato sarà quello di eseguire la prima istruzione eseguibile della funzione, invece dalla finestra contenente il codice disassemblato sarà eseguita l'istruzione assembler corrispondente. Nel caso in cui l'istruzione corrente è una call, il cursore del debug si posizionerà sulla view contenente il codice sorgente C. Comunque è opportuno non abusarne dal momento che superato i quattro annidamenti, specie se le funzioni sono abbastanza complesse, si inizia a dimenticare le prime chiamate. A questo scopo può essere un valido aiuto visualizzare la finestra Hardware Stack in cui sono visualizzate le chiamate effettuate (menu View-->Hardware Stack). Dopo l’esecuzione del comando sarà aggiornato i valori delle variabili/registri/watch di tutte le finestre aperte nell’IDE.
Comando Step Over
Tale comando rappresentato dall’iconanella toolbar esegue un’istruzione oppure una chiamata ad una funzione senza entrare all’interno della stessa. E’ possibile utilizzare anche il tasto F8 oppure il menu DebuggerStep Over. Eseguendo questo comando dalla view contenente il codice sorgente C, il risultato sarà quello di eseguire la istruzione corrente, invece dalla finestra contenente il codice disassemblato sarà eseguita l'istruzione assembler corrispondente. E’ il comando fondamentale per effettuare il debugging di un programma step by step. Dopo l’esecuzione del comando sarà aggiornato i valori delle variabili/registri/watch di tutte le finestre aperte nell’IDE.
Comando Step Out
Tale comando rappresentato dall’icona nella toolbar consente di uscire dalla funzione corrente e di posizionarsi all’istruzione successiva della funzione chiamante. Una volta entrati in una funzione è possibile che non sia più necessario rimanerne all’interno e proseguire il debugging con l’istruzione successiva alla chiamata. In tale circostanza l’uso di tale comando evita di mettere un breakpoint alla fine della funzione, di lanciare il comando Run, di togliere il breakpoint e di dare il comando Step Over per uscire definitivamente dalla funzione. Eseguendo questo comando dalla view contenente il codice sorgente C, il risultato sarà quello di eseguire l'istruzione successiva alla funzione su cui si stava in debug, invece dalla finestra contenente il codice disassemblato sarà eseguita l'istruzione successiva alla funzione su cui si stava in debug posizionandosi nella view contenente il codice C.Dopo l’esecuzione del comando sarà aggiornato i valori delle variabili/registri/watch di tutte le finestre aperte nell’IDE.
Comando Reset
Tale comando rappresentato dall’icona nella toolbar effettua il reset del programma. Il Program Counter viene posizionato all'inizio del programma ed è possibile ricominciare una nuova simulazione. E’ possibile utilizzare anche il tasto F6 oppure il menu Debugger-->Reset-->Processor Reset.Comando Breakpoints
Tale comando rappresentato dall’icona nella toolbar visualizza una dialog per potere inserire dei breakpoints. E’ possibile utilizzare anche il tasto F2 oppure il menu Debugger-->Breakpoints…Tramite questo comando è possibile inserire un breakpoint semplice ad un particolare indirizzo di memoria utilizzando la seguente dialog.
Inserendo un valore numerico nel campo “Break at:” e premendo il tasto Return, questo viene aggiunto alla lista “Program Memory Breakpoints:”. Premendo il tasto “OK” vengono creati i breakpoint agli indirizzi specificati. E’ possibile specificare anche un path realativo ad un file seguito da una virgola ed un valore numerico secondo il seguente formato “Path\File,NumeroRiga”.
In questo caso la finestra contenente il codice sorgente avrà il seguente aspetto
da cui si evince che i breakpoint hanno la B su fondo giallo. Osservando il codice disassemblato avremo la seguente situazione
E’ anche possibile inserire dei breakpoint utilizzando il mouse. Posizionatosi sulla riga di interesse ed effettuando il doppio click si inserisce il breakpoint, rieffettuando il doppio click lo si elimina. Un altro metodo consiste nell'utilizzare il tasto destro del mouse e selezionare “Set Breakpoint”. In questo caso avremo la B su fondo rosso che ci indica la presenza del break.
Nel disassemblato avremo
Nel caso in cui inseriremo il breakpoint partendo dal disassemblato oppure dall'indirizzo del codice macchina avremo la seguente rappresentazione
da cui si evince che la presenza del breakpoint con fondo giallo indica la presenza di un breakpoint impostato partendo dall’indirizzo ed a cui a più istruzioni assembler corrisponde un’unica istruzione a livello di sorgente (linguaggio C).
Comando Run To Cursor
Tale comando attivabile tramite il tasto destro del mouse, consente di eseguire le istruzioni fino al punto in cui è posizionato il cursore dell'editor. In pratica corrisponde all’avere impostato un breakpoint nel punto in cui si è posizionati, lanciato il comando Run ed eliminato il breakpoint impostato precedentemente.
Comando Set PC at Cursor
Tale comando attivabile tramite il tasto destro del mouse, consente di posizionare il cursore del debugger all’istruzione corrente. In pratica inserisce nel Program Counter l’indirizzo dell’istruzione su cui si è posizionato.
Stopwatch
La finestra Stopwatch, attivabile tramite il menu Debug-->Stopwatch, consente di cronometrare sezioni di codice quando sono in esecuzione nel simulatore. I calcoli dello Stopwatch si basano sulle istruzioni eseguite e sui valori impostati per la frequenza del processore. Il tempo totale viene calcolato in base al numero delle istruzioni ciclo. In genere è usata per valutare le differenze di tempo che intercorre tra due istruzioni o per calcolare praticamente i cicli di ritardo che utilizzano istruzioni for/while.
Il tipico utilizzo prevede l'impostazione di due breakpoint a monte ed a valle delle istruzioni da monitorare. Lanciato il comando run, questo si ferma al primo breakpoint. Si azzera il campo Time della finestra Watch tramite il button Zero. Si rilancia il comando run e, quando il cursore del debugger si ferma al secondo breakpoint, si legge il valore numerico di interesse.
Watch
La finestra Watch, attivabile tramite il menu Vew-->Watch, consente di vedere e di modificare i contenuti dei registri del microcontrollore e le variabili del programma.
E’ possibile avere quattro tab per potere impostare diverse finestre per la visualizzazione delle variabili/registri di interesse. Cliccando sul simbolo di scroll presente ai lati dei button “Add SFR” e “Add Symbol” vengono visualizzate le variabili o registri che possono essere monitorati/modificati.
Una volta selezionata una variabile, cliccando sul button “Add Symbol” (oppure su “Add SFR” se si è selezionato un registro), verrà visualizzata nella lista la variabile o il registro.
Cliccando sul campo “Value” associato alla variabile che si vuole modificare, è possibile inserire il valore desiderato.
E’ opportuno osservare che l’operazione di modifica deve essere compatibile con la variabile/registro destinatario della modifica. Modificando il valore di una variabile/registro è possibile effettuare una fault injection ed osservare il comportamento del software a fronte di tale modifica. I passi da seguire possono essere:
- Inserimento di un breakpoint in un punto di interesse e lancio del programma.
- Una volta che il programma ha raggiunto il breakpoint, modificare manualmente il valore della variabile/registro tramite la finestra watch.
- Premere il tasto run ed osservare il comportamento del software.
Tale tecnica di simulare un fault tramite la modifica manuale di una variabile/registro è efficace se non vi sono aspetti legati al sincronismo con altri eventi e se l’interruzione del programma non comporta la creazione di uno stato completamente errato. Un altro criterio è quello di introdurre del codice che agisce in modo da alterare il contenuto di una variabile/registro secondo le nostre aspettative, ma anche in questo caso vi sono delle controindicazioni. E possibile modificare anche il campo “Symbol Name” inserendo una stringa a piacere. Se tale stringa non ha un corrispondente nel programma, il campo “Address” non sarà valorizzato (contiene l’indirizzo della varibile/registro avente come identificativo il valore del campo “Symbol Name”). Per default le variabili/registri presenti nella view Watch sono aggiornati quando il programma si ferma. Per aggiornarle in real time (esempio durante il comando Animate) bisogna abilitare tale funzionalità andando in Debugger-->Settings…-->Animation/Realtime Updates ed spuntando il campo Enable Realtime watch updates.
Cliccando con il tasto desto del mouse viene visualizzato il seguente menu:
Utilizzando i vari item è possibile salvare in un file i valori ed i nomi delle variabili consentendone di tenere traccia anche quando è terminato il test. L'item Output To File... consente di salvare il contenuto della lista, a partire dall'elemento selezionato in poi, in un formato testo facilmente consultabile. La possibilità di monitorare l'evoluzione del microcontrollore e delle variabili/registri, sia graficamente e sia con un formato tabellare, utilizzando varie finestre messe a disposizione dall'ambiente integrato sarà affrontato successivamente in maniera più completa.
Conclusioni
L’utilizzo di questi comandi fondamentali sono abbastanza semplici. E’ sufficiente creare un progetto qualsiasi, scrivere qualche funzione e fare un pò di pratica con i vari comandi e con le finestre di visualizzazione. Nonostante la loro semplicità, sono molto utilizzati e consentono di acquisire una buona familiarità con il microcontrollore e l'esecuzione del programma. Ciononostante si evince che la realizzazione di breakpoints sulla base di determinati valori assunti da una variabile/registro con i comandi descritti non è possibile. Cioè si possono realizzare tali breakpoints a patto di inserire del codice ad hoc all'interno del programma, alterando il codice oggetto su cui eseguire i test ed effettuando una gestione di tali modifiche. Per ovviare a tale situazione bisogna trattare i Breakpoint Complessi, che è un argomento della prossima lezione. Con tali comandi è possibile realizzare dei breakpoints in base al verificarsi di determinate condizioni più o meno complesse consentendo di analizzare l’evoluzione del microcontrollore a partire da un particolare stato.

Elettrotecnica e non solo (admin)
Un gatto tra gli elettroni (IsidoroKZ)
Esperienza e simulazioni (g.schgor)
Moleskine di un idraulico (RenzoDF)
Il Blog di ElectroYou (webmaster)
Idee microcontrollate (TardoFreak)
PICcoli grandi PICMicro (Paolino)
Il blog elettrico di carloc (carloc)
DirtEYblooog (dirtydeeds)
Di tutto... un po' (jordan20)
AK47 (lillo)
Esperienze elettroniche (marco438)
Telecomunicazioni musicali (clavicordo)
Automazione ed Elettronica (gustavo)
Direttive per la sicurezza (ErnestoCappelletti)
EYnfo dall'Alaska (mir)
Apriamo il quadro! (attilio)
H7-25 (asdf)
Passione Elettrica (massimob)
Elettroni a spasso (guidob)
Bloguerra (guerra)