Precedente: , Su: Esempio di sessione di debug   [Contenuti][Indice]


14.2.2 Trovare il bug

Poniamo di avere un problema usando una versione (difettosa) di uniq.awk in modalità “salta-campi”, perché sembra non trovare le righe che sono identiche a condizione di saltare il primo campo, come:

awk, ecco un programma meraviglioso!
gawk, ecco un programma meraviglioso!

Questo potrebbe accadere se noi pensassimo (come in C) che i campi in un record siano numerati prendendo come base lo zero, per cui, invece di scrivere:

campi_ultima = join(vettore_ultima, contatore_file+1, n)
campi_corrente = join(vettore_corrente, contatore_file+1, m)

abbiamo scritto:

campi_ultima = join(vettore_ultima, contatore_file, n)
campi_corrente = join(vettore_corrente, contatore_file, m)

La prima cosa da fare quando si tenta di indagare su un problema come questo è quella di mettere un punto d’interruzione (breakpoint) nel programma, in modo da poterlo vedere al lavoro e catturare quello che non va. Una posizione ragionevole per un punto d’interruzione in uniq.awk è all’inizio della funzione se_sono_uguali(), che confronta la riga corrente con la precedente. Per impostare il punto d’interruzione, usare il comando b (breakpoint):

gawk> b se_sono_uguali
-| Breakpoint 1 impostato al file `uniq.awk', riga 63

Il debugger mostra il file e il numero di riga dove si trova il punto d’interruzione. Ora bisogna immettere ‘r’ o ‘run’ e il programma viene eseguito fino al primo punto d’interruzione:

gawk> r
-| Partenza del programma:
-| Mi fermo in Rule ...
-| Breakpoint 1, se_sono_uguali(n, m, campi_ultima, campi_corrente,
-|                              vettore_ultima, vettore_corrente)
-| a `uniq.awk':63
-| 63          if (contatore_file == 0 && conta_caratteri == 0)
gawk>

Ora possiamo osservare cosa accade all’interno del nostro programma. Prima di tutto, vediamo come siamo arrivati a questo punto. Sulla riga di comando battiamo ‘bt’ (che sta per “backtrace”), e il debugger risponde con un listato degli stack frame correnti:

gawk> bt
-| #0  se_sono_uguali(n, m, campi_ultima, campi_corrente,
-|                    vettore_ultima, vettore_corrente)
-| a `uniq.awk':63
-| #1  in main() a `uniq.awk':88

Questo ci dice che la funzione se_sono_uguali() è stata chiamata dal programma principale alla riga 88 del file uniq.awk. (Questo non sorprende, perché è questa l’unica chiamata a se_sono_uguali() nel programma, però in programmi più complessi, sapere chi ha chiamato una funzione e con quali parametri può essere la chiave per trovare l’origine del problema.)

Ora che siamo in se_sono_uguali(), possiamo iniziare a guardare i valori di alcune variabili. Immaginiamo di battere ‘p n’ (p sta per print [stampa]). Ci aspetteremo di vedere il valore di n, un parametro di se_sono_uguali(). In realtà, il debugger ci dà:

gawk> p n
-| n = untyped variable

In questo caso, n è una variabile locale non inizializzata, perché la funzione è stata chiamata senza argomenti (vedi la sezione Chiamate di funzione).

Una variabile più utile da visualizzare potrebbe essere la seguente:

gawk> p $0
-| $0 = "gawk, ecco un programma meraviglioso!"

All’inizio questo potrebbe lasciare un tantino perplessi, perché è la seconda riga dell’input del test. Vediamo NR:

gawk> p NR
-| NR = 2

Come si può vedere, se_sono_uguali() è stata chiamata solo per la seconda riga del file. Naturalmente, ciò accade perché il nostro programma contiene una regola per ‘NR == 1’:

NR == 1 {
    ultima = $0
    next
}

Bene, controlliamo che questa funzioni correttamente:

gawk> p ultima
-| ultima = "awk, ecco un programma meraviglioso!"

Tutto ciò che è stato fatto fin qui ha verificato che il programma funziona come previsto fino alla chiamata a se_sono_uguali() compresa; quindi il problema dev’essere all’interno di questa funzione. Per indagare ulteriormente, iniziamo a “scorrere una ad una” le righe di se_sono_uguali(). Cominciamo col battere ‘n’ (per “next” [successivo]):

gawk> n
-| 66          if (contatore_file > 0) {

Questo ci dice che gawk ora è pronto per eseguire la riga 66, che decide se assegnare alle righe il trattamento speciale “salta-campi” indicato dall’opzione sulla riga di comando -1. (Si noti che abbiamo saltato da dov’eravamo prima, alla riga 63, a qui, perché la condizione nella riga 63, ‘if (contatore_file == 0 && conta_caratteri == 0)’, era falsa.)

Continuando a scorrere le righe, ora raggiungiamo la divisione del record corrente e dell’ultimo:

gawk> n
-| 67              n = split(ultima, vettore_ultima)
gawk> n
-| 68              m = split($0, vettore_corrente)

A questo punto, potremmo stare a vedere in quante parti il nostro record è stato suddiviso, quindi proviamo a osservare:

gawk> p n m vettore_ultima vettore_corrente
-| n = 5
-| m = untyped variable
-| vettore_ultima = array, 5 elements
-| vettore_corrente = untyped variable

(Il comando p può accettare più argomenti, analogamente all’istruzione di awk print.)

Questo ci lascia piuttosto perplessi. Tutto ciò che abbiamo trovato è che ci sono cinque elementi in vettore_ultima; m e vettore_corrente non hanno valori perché siamo alla riga 68 che non è ancora stata eseguita. Questa informazione è abbastanza utile (ora sappiamo che nessuna delle parole è stata lasciata fuori accidentalmente), ma sarebbe desiderabile vedere i valori del vettore.

Una prima possibilità è quella di usare degli indici:

gawk> p vettore_ultima[0]
-| "0" non presente nel vettore `vettore_ultima'

Oops!

gawk> p vettore_ultima[1]
-| vettore_ultima["1"] = "awk,"

Questo metodo sarebbe piuttosto lento per un vettore con 100 elementi, per cui gawk fornisce una scorciatoia (che fa venire in mente un altro linguaggio che non nominiamo):

gawk> p @vettore_ultima
-| vettore_ultima["1"] = "awk,"
-| vettore_ultima["2"] = "ecco"
-| vettore_ultima["3"] = "un"
-| vettore_ultima["4"] = "programma"
-| vettore_ultima["5"] = "meraviglioso!"

Finora, sembra che tutto vada bene. Facciamo un altro passo, o anche due:

gawk> n
-| 69              campi_ultima = join(vettore_ultima, contatore_file, n)
gawk> n
-| 70              campi_corrente = join(vettore_corrente, contatore_file, m)

Bene, eccoci arrivati al nostro errore (ci spiace di aver rovinato la sorpresa). Quel che avevamo in mente era di unire i campi a partire dal secondo per creare il record virtuale da confrontare, e se il primo campo aveva il numero zero, questo avrebbe funzionato. Vediamo quel che abbiamo finora:

gawk> p campi_ultima campi_corrente
-| campi_ultima = "awk, ecco un programma meraviglioso!"
-| campi_corrente = "gawk, ecco un programma meraviglioso!"

Ehi! queste frasi suonano piuttosto familiari! Sono esattamente i nostri record di input originali, inalterati. Pensandoci un po’ (il cervello umano è ancora il miglior strumento di debug), ci si rende conto che eravamo fuori di uno!

Usciamo dal debugger:

gawk> q
-| Il programma è in esecuzione. Esco comunque (y/n)? y

Quindi modifichiamo con un editore di testo:

campi_ultima = join(vettore_ultima, contatore_file+1, n)
campi_corrente = join(vettore_corrente, contatore_file+1, m)

e il problema è risolto!


Precedente: , Su: Esempio di sessione di debug   [Contenuti][Indice]