Precedente: Invocazione del debugger, Su: Esempio di sessione di debug [Contenuti][Indice]
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: Invocazione del debugger, Su: Esempio di sessione di debug [Contenuti][Indice]