2010-07-09 7 views
6

Quando il debug una funzione io di solito usoConsigli per il debug "dinamico/interattivo" delle funzioni in R?

library(debug) 
mtrace(FunctionName) 
FunctionName(...) 

e che funziona abbastanza bene per me.

Tuttavia, a volte sto tentando di eseguire il debug di una funzione complessa che non conosco. Nel qual caso, posso scoprire che all'interno di quella funzione c'è un'altra funzione che vorrei "entrare" ("debug") - così da capire meglio come funziona l'intero processo.

Quindi un modo per farlo sarebbe quello di fare:

library(debug) 
mtrace(FunctionName) 
FunctionName(...) 
# when finding a function I want to debug inside the function, run again: 
mtrace(FunctionName.SubFunction) 

La domanda è - c'è un/modo migliore più intelligente per fare il debug interattivo (come ho descritto) che potrei mancare?

p.s: Sono a conoscenza del fatto che sono state poste varie domande sull'argomento in SO (vedere here). Eppure non sono riuscito a trovare una soluzione/una soluzione simile a quello che ho chiesto qui.

risposta

5

Non completamente sicuro del caso d'uso, ma quando si verifica un problema, è possibile chiamare la funzione traceback(). Questo mostrerà il percorso della tua chiamata di funzione attraverso la pila fino a quando non ha colpito il suo problema. Potresti, se fossi incline a scendere dall'alto, chiamare debug su ciascuna delle funzioni indicate nell'elenco prima di effettuare la chiamata di funzione. Quindi dovresti percorrere l'intero processo dall'inizio.

Ecco un esempio di come si potrebbe fare questo in modo più sistematico, con la creazione di una funzione al passaggio attraverso di essa:

walk.through <- function() { 
    tb <- unlist(.Traceback) 
    if(is.null(tb)) stop("no traceback to use for debugging") 
    assign("debug.fun.list", matrix(unlist(strsplit(tb, "\\(")), nrow=2)[1,], envir=.GlobalEnv) 
    lapply(debug.fun.list, function(x) debug(get(x))) 
    print(paste("Now debugging functions:", paste(debug.fun.list, collapse=","))) 
} 

unwalk.through <- function() { 
    lapply(debug.fun.list, function(x) undebug(get(as.character(x)))) 
    print(paste("Now undebugging functions:", paste(debug.fun.list, collapse=","))) 
    rm(list="debug.fun.list", envir=.GlobalEnv) 
} 

Ecco un esempio fittizio di usarlo:

foo <- function(x) { print(1); bar(2) } 
bar <- function(x) { x + a.variable.which.does.not.exist } 
foo(2) 

# now step through the functions 
walk.through() 
foo(2) 

# undebug those functions again... 
unwalk.through() 
foo(2) 

IMO, non sembra la cosa più sensata da fare. Ha più senso andare semplicemente nella funzione in cui si verifica il problema (cioè al livello più basso) e lavorare all'indietro.

Ho già delineato la logica dietro questa routine di base in "favorite debugging trick".

+0

Grazie Shane, Potrei usare il tuo codice con mtrace, che potrebbe essere carino in alcuni casi. Ma in generale, prendo il tuo punto riguardo al debug bottom-up. –

+0

Ciao Shane, ripensandoci. Possiamo estrarre la lista di funzioni da traceback in modo da poter eseguire la funzione solo su di essi? –

+1

Questo è esattamente ciò che fa la mia funzione walk.through. – Shane

5

Mi piace options(error=recover) come dettagliato previously on SO. Le cose si fermano al punto di errore e si può ispezionare.

+0

Grazie Dirk. La mia domanda derivava da casi dove il bug nel mio codice proveniva da passaggi precedenti con casi finali strani. Sono d'accordo che la prima cosa dovrebbe essere la tua opzione. E se siamo in argomento, pensi che potrebbe essere possibile chiedere alla funzione di recuperare usando qualcosa di simile ? a mtrace (da {debug}) invece del browser di base() –

+0

hai provato 'opzioni (errore = mtrace)' – Shane

+0

Ora ho fatto, gli errori con:? [1] 1 errore in bar (2): oggetto 'a.variable.which.does.not.exist' non trovato Erro r durante il wrapup: primo argomento non valido –

3

(Sono l'autore del pacchetto 'debug' in cui vive 'mtrace')

Se la definizione di 'subfunction' vive fuori 'MyFunction', allora si può solo mtrace 'sottofunzione' e don' t necessario mtrace 'MyFunction'. E le funzioni corrono più velocemente se non sono 'mtrace'd, quindi è meglio mtrace solo il minimo necessario. (Ma probabilmente conosci già queste cose!)

Se "MyFunction" è definito solo all'interno di "SubFunction", un trucco che potrebbe aiutare è utilizzare un punto di interruzione condizionale in "MyFunction". Avrai bisogno di 'mtrace (MyFunction)', quindi eseguirlo, e quando appare la finestra di debug, scopri quale linea 'MyFunction' è definita in. Dire che è la linea 17.Poi il seguente dovrebbe funzionare:

D (n)> bp (1, F) # non preoccupatevi che mostra la finestra per MyFunction nuovo D (n)> bp (18, {mtrace (sottofunzione); FALSE}) D (n)> go()

Dovrebbe essere chiaro cosa fa (o lo sarà se lo provate).

Gli unici svantaggi sono: la necessità di rifarlo ogni volta che si modifica il codice di "MyFunction", e; il rallentamento che potrebbe verificarsi con "MyFunction" stesso in stato di fermo.

Si potrebbe anche sperimentare l'aggiunta di un argomento "debug.sub" a "MyFunction", che di default è FALSE. Nel codice di 'MyFunction', quindi aggiungere questa riga immediatamente dopo la definizione di 'Sottofunzione':

se (debug.sub) mtrace (sottofunzione)

che evita qualsiasi necessità di mtrace 'MyFunction' stessa, ma richiede di essere in grado di cambiare il suo codice.

+0

Ciao Mark, sono molto contento che tu abbia partecipato alla discussione. Sostengo fortemente il tuo pacchetto e mi diverto ad usarlo (quasi quotidianamente). I suggerimenti che hai dato sono interessanti. Dalla discussione sopra c'è un'abilità che sto cercando di capire come fare con mtrace - e cioè, "mtrace" automatico delle funzioni ALL (o di selezionate) coinvolte in un bug. La funzione che Shane ha dato nella sua risposta lo dimostra chiaramente. Il bisogno arriva quando l'insetto ha origine attorno al punto di rottura. Grazie ancora per tutto il codice e il tempo! –

Problemi correlati