Il debug tramite la creazione di tracce di esecuzione è una tecnica molto sottovalutata. È uno strumento prezioso per il kit di strumenti dei programmatori. Come ogni strumento, ha la sua area di applicazione; altre volte un debugger di break point è più utile o genera una traccia di stack.
Il debugging by trace è più utile
- quando nessun altro debugger è disponibile, per vari motivi.
- quando si interrompe il programma si confonderebbe con i timer.
- durante il debug di un programma multithreading in cui è necessario vedere l'interleaving dei thread.
- quando si sviluppa un (sottile) bug su una lunga sequenza di esecuzione.
La differenza è che un debugger del punto di interruzione fornisce un'istantanea dello stato del programma e la traccia mostra ciò che accade nel tempo. Bug che sono difficili da risolvere con uno strumento possono essere più facili da decifrare con un altro strumento.
Il modo di costruire strumenti di traccia varia con la lingua di destinazione. Ciò che è costante sono le doti da fornire. Ecco il mio elenco di funzioni di base
- In grado di essere attivato e disattivato impostando una bandiera. Il valore del flag può essere hardcoded o impostato in fase di esecuzione tramite il debug del codice.
- Per i linguaggi compilati l'effetto del codice di debug dovrebbe essere completamente rimosso. Se disattivato, il codice di debug dovrebbe avere l'effetto no sul programma. Anche il debug del codice può avere bug e dà sempre un risultato in termini di prestazioni.
- Supporta un modo di registrazione che sia stata inserita una funzione o un metodo. Il log dovrebbe contenere il nome del file / nome file, il nome metodo / funzione e ciascun valore argomento.
- Supporta un metodo di registrazione del metodo o l'uscita della funzione, incluso il valore di ritorno.
- Supporto per la registrazione di un messaggio arbitrario.
- Supporta la registrazione su file o console.
Altre funzioni avanzate includono
- Rientro di ciascun messaggio di registro in base all'intensità della chiamata.
- Prefisso di ogni messaggio di registro con una data e un timestamp.
- Supporta più file di registro in cui vengono conservati N.
- Supporto per una dimensione massima del file di registro, eseguendo il wrapping all'inizio o passando a un nuovo file.
- Supporto per i livelli di registrazione, da nessuno a estremamente dettagliato.
- Possibilità di selezionare la registrazione per file sorgente / nome classe o nome funzione.
In C o C ++, tutto questo può essere ottenuto con la compilazione condizionale di macro nei file include. Impostando un flag del compilatore, è possibile disattivare completamente le macro senza toccare il codice sorgente, una capacità importante da avere. Il codice può essere molto più compatto in C ++ perché possiamo sfruttare il polimorfismo.
In Java, possiamo implementare il codice di debug in una coppia di file jar, uno con tutti i metodi di debug implementati e l'altro con metodi vuoti. Questo sfrutta le capacità di ottimizzazione della JVM. Ovviamente dovrebbe essere preso in considerazione Log4J .
Nei linguaggi di scripting è possibile combinare le tecniche sopra riportate per ottenere lo stesso effetto. Per html / javascript puoi usare il tag script per includere la sorgente di debugging javascript richiesta. Se la pagina viene generata da PHP, puoi includere in modo condizionale il codice di debug nella pagina.
Le funzionalità di base possono essere costruite in circa 50 righe di codice (come ricordo, dal momento che non sono come la mia macchina da scrivania), e le funzionalità avanzate possono essere altre 100 circa.
Le funzioni di traccia delle funzioni descritte sopra possono essere implementate in Javascript (usando la tua funzione dbmsg
) come (non testato):
function dbcall( /* name, args */ ) {
var msg = arguments[0] + '( ';
for (var i = 1; i < arguments.length - 1; i++)
msg += arguments[i] + ', ';
msg += arguments[arguments.length - 1] + ' )';
dbmsg( msg );
}
function dbretn( /* name, value */ ) {
var msg = arguments[0] + ' returns ';
msg += arguments[1];
dbmsg( msg );
}
In una lingua che supporta i macro possiamo impostare una macro che chiama dbretn
che fa il ritorno, ma in javascript dobbiamo aggiungere la chiamata davanti a ogni return
come in
...
dbretn( 'MyFunction', value );
return value;
Il listener storage.changed
che mostri nella domanda è un buon esempio di come tali strutture di debug dovrebbero essere adattate a ciascun ambiente.